diff options
author | James.cf Lin <jamescflin@google.com> | 2019-10-01 14:51:33 +0800 |
---|---|---|
committer | James Lin <jamescflin@google.com> | 2019-10-01 19:32:22 +0000 |
commit | 79e54d9407c76d260a4e8bf7175f99a3339be673 (patch) | |
tree | f956687bec5f2f5014d5da1c01984d9cd215a8b3 | |
parent | 8d6c18b5e9642dac8778e7f323375ab74ba10af4 (diff) | |
download | ims-ndk-sysroot-r21.tar.gz |
[RCS] Initialize RcsFeatureManager and RcsFeatureConnection instancendk-sysroot-r21
Create RcsFeatureManager and RcsFeatureConnection to handle the RCS UCE operation.
Bug: 139260798
Test: Manual
Change-Id: I28b304080a6ef216763512fc24e6f9944e207dde
Merged-In: Ibb1ad42154c76bc3716047e6959ee9e1fd3ec86d
-rw-r--r-- | src/java/com/android/ims/FeatureConnection.java | 210 | ||||
-rw-r--r-- | src/java/com/android/ims/MmTelFeatureConnection.java | 262 | ||||
-rw-r--r-- | src/java/com/android/ims/RcsFeatureConnection.java | 183 | ||||
-rw-r--r-- | src/java/com/android/ims/RcsFeatureManager.java | 104 |
4 files changed, 566 insertions, 193 deletions
diff --git a/src/java/com/android/ims/FeatureConnection.java b/src/java/com/android/ims/FeatureConnection.java new file mode 100644 index 00000000..5bec9df7 --- /dev/null +++ b/src/java/com/android/ims/FeatureConnection.java @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.ims; + +import android.content.Context; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.RemoteException; +import android.telephony.TelephonyManager; +import android.telephony.ims.aidl.IImsMmTelFeature; +import android.telephony.ims.aidl.IImsRcsFeature; +import android.telephony.ims.feature.ImsFeature; +import android.telephony.Rlog; +import android.telephony.SubscriptionManager; +import android.util.Log; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.ims.internal.IImsServiceFeatureCallback; +import com.android.ims.MmTelFeatureConnection.IFeatureUpdate; + +import java.util.concurrent.Executor; + +/** + * Base class of MmTelFeatureConnection and RcsFeatureConnection. + */ +public abstract class FeatureConnection { + protected static final String TAG = "FeatureConnection"; + + protected static boolean sImsSupportedOnDevice = true; + + protected final int mSlotId; + protected final int mFeatureType; + protected Context mContext; + protected IBinder mBinder; + @VisibleForTesting + public Executor mExecutor; + + // We are assuming the feature is available when started. + protected volatile boolean mIsAvailable = true; + // ImsFeature Status from the ImsService. Cached. + protected Integer mFeatureStateCached = null; + protected IFeatureUpdate mStatusCallback; + protected final Object mLock = new Object(); + + public FeatureConnection(Context context, int slotId, int featureType) { + mSlotId = slotId; + mContext = context; + mFeatureType = featureType; + + // Callbacks should be scheduled on the main thread. + if (context.getMainLooper() != null) { + mExecutor = context.getMainExecutor(); + } else { + // Fallback to the current thread. + if (Looper.myLooper() == null) { + Looper.prepare(); + } + mExecutor = new HandlerExecutor(new Handler(Looper.myLooper())); + } + } + + protected TelephonyManager getTelephonyManager() { + return (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); + } + + public void setBinder(IBinder binder) { + synchronized (mLock) { + mBinder = binder; + try { + if (mBinder != null) { + mBinder.linkToDeath(mDeathRecipient, 0); + } + } catch (RemoteException e) { + // No need to do anything if the binder is already dead. + } + } + } + + protected final IBinder.DeathRecipient mDeathRecipient = () -> { + Log.w(TAG, "DeathRecipient triggered, binder died."); + if (mContext != null && Looper.getMainLooper() != null) { + // Move this signal to the main thread, notifying ImsManager of the Binder + // death on another thread may lead to deadlocks. + mContext.getMainExecutor().execute(this::onRemovedOrDied); + return; + } + // No choice - execute on the current Binder thread. + onRemovedOrDied(); + }; + + /** + * Called when the MmTelFeature/RcsFeature has either been removed by Telephony or crashed. + */ + protected void onRemovedOrDied() { + synchronized (mLock) { + if (mIsAvailable) { + mIsAvailable = false; + if (mBinder != null) { + mBinder.unlinkToDeath(mDeathRecipient, 0); + } + if (mStatusCallback != null) { + mStatusCallback.notifyUnavailable(); + } + } + } + } + + @VisibleForTesting + public IImsServiceFeatureCallback getListener() { + return mListenerBinder; + } + + private final IImsServiceFeatureCallback mListenerBinder = + new IImsServiceFeatureCallback.Stub() { + @Override + public void imsFeatureCreated(int slotId, int feature) { + mExecutor.execute(() -> { + handleImsFeatureCreatedCallback(slotId, feature); + }); + } + @Override + public void imsFeatureRemoved(int slotId, int feature) { + mExecutor.execute(() -> { + handleImsFeatureRemovedCallback(slotId, feature); + }); + } + @Override + public void imsStatusChanged(int slotId, int feature, int status) { + mExecutor.execute(() -> { + handleImsStatusChangedCallback(slotId, feature, status); + }); + } + }; + + protected abstract void handleImsFeatureCreatedCallback(int slotId, int feature); + protected abstract void handleImsFeatureRemovedCallback(int slotId, int feature); + protected abstract void handleImsStatusChangedCallback(int slotId, int feature, int status); + + protected void checkServiceIsReady() throws RemoteException { + if (!sImsSupportedOnDevice) { + throw new RemoteException("IMS is not supported on this device."); + } + if (!isBinderReady()) { + throw new RemoteException("ImsServiceProxy is not ready to accept commands."); + } + } + + /** + * @return Returns true if the ImsService is ready to take commands, false otherwise. If this + * method returns false, it doesn't mean that the Binder connection is not available (use + * {@link #isBinderReady()} to check that), but that the ImsService is not accepting commands + * at this time. + * + * For example, for DSDS devices, only one slot can be {@link ImsFeature#STATE_READY} to take + * commands at a time, so the other slot must stay at {@link ImsFeature#STATE_UNAVAILABLE}. + */ + public boolean isBinderReady() { + return isBinderAlive() && getFeatureState() == ImsFeature.STATE_READY; + } + + /** + * @return false if the binder connection is no longer alive. + */ + public boolean isBinderAlive() { + return mIsAvailable && mBinder != null && mBinder.isBinderAlive(); + } + + /** + * @return an integer describing the current Feature Status, defined in + * {@link ImsFeature.ImsState}. + */ + public int getFeatureState() { + synchronized (mLock) { + if (isBinderAlive() && mFeatureStateCached != null) { + return mFeatureStateCached; + } + } + // Don't synchronize on Binder call. + Integer state = retrieveFeatureState(); + synchronized (mLock) { + if (state == null) { + return ImsFeature.STATE_UNAVAILABLE; + } + // Cache only non-null value for feature status. + mFeatureStateCached = state; + } + Log.i(TAG, "getFeatureState - returning " + ImsFeature.STATE_LOG_MAP.get(state)); + return state; + } + + /** + * Internal method used to retrieve the feature status from the corresponding ImsService. + */ + protected abstract Integer retrieveFeatureState(); +} diff --git a/src/java/com/android/ims/MmTelFeatureConnection.java b/src/java/com/android/ims/MmTelFeatureConnection.java index ea7d1e1b..76d12c05 100644 --- a/src/java/com/android/ims/MmTelFeatureConnection.java +++ b/src/java/com/android/ims/MmTelFeatureConnection.java @@ -19,7 +19,6 @@ package com.android.ims; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; -import android.os.Handler; import android.os.IBinder; import android.os.IInterface; import android.os.Looper; @@ -58,16 +57,14 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Set; -import java.util.concurrent.Executor; import java.util.stream.Collectors; /** * A container of the IImsServiceController binder, which implements all of the ImsFeatures that - * the platform currently supports: MMTel and RCS. - * @hide + * the platform currently supports: MMTel */ -public class MmTelFeatureConnection { +public class MmTelFeatureConnection extends FeatureConnection { protected static final String TAG = "MmTelFeatureConnection"; // Manages callbacks to the associated MmTelFeature in mMmTelFeatureConnection. @@ -417,38 +414,14 @@ public class MmTelFeatureConnection { } } - protected final int mSlotId; - protected IBinder mBinder; - private Context mContext; - private Executor mExecutor; - - // We are assuming the feature is available when started. - private volatile boolean mIsAvailable = true; - // ImsFeature Status from the ImsService. Cached. - private Integer mFeatureStateCached = null; private IFeatureUpdate mStatusCallback; - private final Object mLock = new Object(); // Updated by IImsServiceFeatureCallback when FEATURE_EMERGENCY_MMTEL is sent. private boolean mSupportsEmergencyCalling = false; - private static boolean sImsSupportedOnDevice = true; // Cache the Registration and Config interfaces as long as the MmTel feature is connected. If // it becomes disconnected, invalidate. private IImsRegistration mRegistrationBinder; private IImsConfig mConfigBinder; - - private final IBinder.DeathRecipient mDeathRecipient = () -> { - Log.w(TAG, "DeathRecipient triggered, binder died."); - if (mContext != null && Looper.getMainLooper() != null) { - // Move this signal to the main thread, notifying ImsManager of the Binder - // death on another thread may lead to deadlocks. - mContext.getMainExecutor().execute(this::onRemovedOrDied); - return; - } - // No choice - execute on the current Binder thread. - onRemovedOrDied(); - }; - private final ImsRegistrationCallbackAdapter mRegistrationCallbackManager; private final CapabilityCallbackManager mCapabilityCallbackManager; private final ProvisioningCallbackManager mProvisioningCallbackManager; @@ -461,7 +434,7 @@ public class MmTelFeatureConnection { return serviceProxy; } - TelephonyManager tm = getTelephonyManager(context); + TelephonyManager tm = serviceProxy.getTelephonyManager(); if (tm == null) { Rlog.w(TAG, "create: TelephonyManager is null!"); // Binder can be unset in this case because it will be torn down/recreated as part of @@ -481,10 +454,6 @@ public class MmTelFeatureConnection { return serviceProxy; } - public static TelephonyManager getTelephonyManager(Context context) { - return (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); - } - public interface IFeatureUpdate { /** * Called when the ImsFeature has changed its state. Use @@ -499,99 +468,16 @@ public class MmTelFeatureConnection { void notifyUnavailable(); } - private final IImsServiceFeatureCallback mListenerBinder = - new IImsServiceFeatureCallback.Stub() { - - @Override - public void imsFeatureCreated(int slotId, int feature) { - mExecutor.execute(() -> { - // The feature has been enabled. This happens when the feature is first created and - // may happen when the feature is re-enabled. - synchronized (mLock) { - if(mSlotId != slotId) { - return; - } - switch (feature) { - case ImsFeature.FEATURE_MMTEL: { - if (!mIsAvailable) { - Log.i(TAG, "MmTel enabled on slotId: " + slotId); - mIsAvailable = true; - } - break; - } - case ImsFeature.FEATURE_EMERGENCY_MMTEL: { - mSupportsEmergencyCalling = true; - Log.i(TAG, "Emergency calling enabled on slotId: " + slotId); - break; - } - } - } - }); - } - - @Override - public void imsFeatureRemoved(int slotId, int feature) { - mExecutor.execute(() -> { - synchronized (mLock) { - if (mSlotId != slotId) { - return; - } - switch (feature) { - case ImsFeature.FEATURE_MMTEL: { - Log.i(TAG, "MmTel removed on slotId: " + slotId); - onRemovedOrDied(); - break; - } - case ImsFeature.FEATURE_EMERGENCY_MMTEL: { - mSupportsEmergencyCalling = false; - Log.i(TAG, "Emergency calling disabled on slotId: " + slotId); - break; - } - } - } - }); - } - - @Override - public void imsStatusChanged(int slotId, int feature, int status) { - mExecutor.execute(() -> { - synchronized (mLock) { - Log.i(TAG, "imsStatusChanged: slot: " + slotId + " feature: " - + ImsFeature.FEATURE_LOG_MAP.get(feature) + - " status: " + ImsFeature.STATE_LOG_MAP.get(status)); - if (mSlotId == slotId && feature == ImsFeature.FEATURE_MMTEL) { - mFeatureStateCached = status; - if (mStatusCallback != null) { - mStatusCallback.notifyStateChanged(); - } - } - } - }); - } - }; - public MmTelFeatureConnection(Context context, int slotId) { - mSlotId = slotId; - mContext = context; - // Callbacks should be scheduled on the main thread. - if (context.getMainLooper() != null) { - mExecutor = context.getMainExecutor(); - } else { - // Fallback to the current thread. - if (Looper.myLooper() == null) { - Looper.prepare(); - } - mExecutor = new HandlerExecutor(new Handler(Looper.myLooper())); - } + super(context, slotId, ImsFeature.FEATURE_MMTEL); + mRegistrationCallbackManager = new ImsRegistrationCallbackAdapter(context, mLock); mCapabilityCallbackManager = new CapabilityCallbackManager(context, mLock); mProvisioningCallbackManager = new ProvisioningCallbackManager(context, mLock); } - /** - * Called when the MmTelFeature has either been removed by Telephony or crashed. - */ - private void onRemovedOrDied() { + @Override + protected void onRemovedOrDied() { synchronized (mLock) { mRegistrationCallbackManager.close(); mCapabilityCallbackManager.close(); @@ -618,7 +504,7 @@ public class MmTelFeatureConnection { return mRegistrationBinder; } } - TelephonyManager tm = getTelephonyManager(mContext); + TelephonyManager tm = getTelephonyManager(); // We don't want to synchronize on a binder call to another process. IImsRegistration regBinder = tm != null ? tm.getImsRegistration(mSlotId, ImsFeature.FEATURE_MMTEL) : null; @@ -639,7 +525,7 @@ public class MmTelFeatureConnection { return mConfigBinder; } } - TelephonyManager tm = getTelephonyManager(mContext); + TelephonyManager tm = getTelephonyManager(); IImsConfig configBinder = tm != null ? tm.getImsConfig(mSlotId, ImsFeature.FEATURE_MMTEL) : null; synchronized (mLock) { @@ -651,27 +537,71 @@ public class MmTelFeatureConnection { return mConfigBinder; } - public boolean isEmergencyMmTelAvailable() { - return mSupportsEmergencyCalling; + @Override + protected void handleImsFeatureCreatedCallback(int slotId, int feature) { + // The feature has been enabled. This happens when the feature is first created and + // may happen when the feature is re-enabled. + synchronized (mLock) { + if(mSlotId != slotId) { + return; + } + switch (feature) { + case ImsFeature.FEATURE_MMTEL: { + if (!mIsAvailable) { + Log.i(TAG, "MmTel enabled on slotId: " + slotId); + mIsAvailable = true; + } + break; + } + case ImsFeature.FEATURE_EMERGENCY_MMTEL: { + mSupportsEmergencyCalling = true; + Log.i(TAG, "Emergency calling enabled on slotId: " + slotId); + break; + } + } + } } - public IImsServiceFeatureCallback getListener() { - return mListenerBinder; + @Override + protected void handleImsFeatureRemovedCallback(int slotId, int feature) { + synchronized (mLock) { + if (mSlotId != slotId) { + return; + } + switch (feature) { + case ImsFeature.FEATURE_MMTEL: { + Log.i(TAG, "MmTel removed on slotId: " + slotId); + onRemovedOrDied(); + break; + } + case ImsFeature.FEATURE_EMERGENCY_MMTEL: { + mSupportsEmergencyCalling = false; + Log.i(TAG, "Emergency calling disabled on slotId: " + slotId); + break; + } + } + } } - public void setBinder(IBinder binder) { + @Override + protected void handleImsStatusChangedCallback(int slotId, int feature, int status) { synchronized (mLock) { - mBinder = binder; - try { - if (mBinder != null) { - mBinder.linkToDeath(mDeathRecipient, 0); + Log.i(TAG, "imsStatusChanged: slot: " + slotId + " feature: " + + ImsFeature.FEATURE_LOG_MAP.get(feature) + + " status: " + ImsFeature.STATE_LOG_MAP.get(status)); + if (mSlotId == slotId && feature == ImsFeature.FEATURE_MMTEL) { + mFeatureStateCached = status; + if (mStatusCallback != null) { + mStatusCallback.notifyStateChanged(); } - } catch (RemoteException e) { - // No need to do anything if the binder is already dead. } } } + public boolean isEmergencyMmTelAvailable() { + return mSupportsEmergencyCalling; + } + /** * Opens the connection to the {@link MmTelFeature} and establishes a listener back to the * framework. Calling this method multiple times will reset the listener attached to the @@ -891,32 +821,14 @@ public class MmTelFeatureConnection { } /** - * @return an integer describing the current Feature Status, defined in - * {@link ImsFeature.ImsState}. + * @param c Callback that will fire when the feature status has changed. */ - public int getFeatureState() { - synchronized (mLock) { - if (isBinderAlive() && mFeatureStateCached != null) { - return mFeatureStateCached; - } - } - // Don't synchronize on Binder call. - Integer state = retrieveFeatureState(); - synchronized (mLock) { - if (state == null) { - return ImsFeature.STATE_UNAVAILABLE; - } - // Cache only non-null value for feature status. - mFeatureStateCached = state; - } - Log.i(TAG, "getFeatureState - returning " + ImsFeature.STATE_LOG_MAP.get(state)); - return state; + public void setStatusCallback(IFeatureUpdate c) { + mStatusCallback = c; } - /** - * Internal method used to retrieve the feature status from the corresponding ImsService. - */ - private Integer retrieveFeatureState() { + @Override + protected Integer retrieveFeatureState() { if (mBinder != null) { try { return getServiceInterface(mBinder).getFeatureState(); @@ -927,42 +839,6 @@ public class MmTelFeatureConnection { return null; } - /** - * @param c Callback that will fire when the feature status has changed. - */ - public void setStatusCallback(IFeatureUpdate c) { - mStatusCallback = c; - } - - /** - * @return Returns true if the ImsService is ready to take commands, false otherwise. If this - * method returns false, it doesn't mean that the Binder connection is not available (use - * {@link #isBinderReady()} to check that), but that the ImsService is not accepting commands - * at this time. - * - * For example, for DSDS devices, only one slot can be {@link ImsFeature#STATE_READY} to take - * commands at a time, so the other slot must stay at {@link ImsFeature#STATE_UNAVAILABLE}. - */ - public boolean isBinderReady() { - return isBinderAlive() && getFeatureState() == ImsFeature.STATE_READY; - } - - /** - * @return false if the binder connection is no longer alive. - */ - public boolean isBinderAlive() { - return mIsAvailable && mBinder != null && mBinder.isBinderAlive(); - } - - private void checkServiceIsReady() throws RemoteException { - if (!sImsSupportedOnDevice) { - throw new RemoteException("IMS is not supported on this device."); - } - if (!isBinderReady()) { - throw new RemoteException("ImsServiceProxy is not ready to accept commands."); - } - } - private IImsMmTelFeature getServiceInterface(IBinder b) { return IImsMmTelFeature.Stub.asInterface(b); } diff --git a/src/java/com/android/ims/RcsFeatureConnection.java b/src/java/com/android/ims/RcsFeatureConnection.java new file mode 100644 index 00000000..7f65355b --- /dev/null +++ b/src/java/com/android/ims/RcsFeatureConnection.java @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.ims; + +import android.annotation.NonNull; +import android.content.Context; +import android.os.IBinder; +import android.os.RemoteException; +import android.telephony.ims.aidl.IRcsFeatureListener; +import android.telephony.ims.feature.ImsFeature; +import android.telephony.Rlog; +import android.telephony.TelephonyManager; +import android.telephony.ims.aidl.IImsRcsFeature; +import android.util.Log; + +import com.android.internal.annotations.VisibleForTesting; + +/** + * A container of the IImsServiceController binder, which implements all of the RcsFeatures that + * the platform currently supports: RCS + */ +public class RcsFeatureConnection extends FeatureConnection { + private static final String TAG = "RcsFeatureConnection"; + + public static @NonNull RcsFeatureConnection create(Context context , int slotId) { + RcsFeatureConnection serviceProxy = new RcsFeatureConnection(context, slotId); + if (!ImsManager.isImsSupportedOnDevice(context)) { + // Return empty service proxy in the case that IMS is not supported. + sImsSupportedOnDevice = false; + return serviceProxy; + } + + if (!sRcsFeatureManagerProxy.isRcsUceSupportedByCarrier(context, slotId)) { + // Return empty service proxy in the case that RCS feature is not supported. + Rlog.w(TAG, "create: RCS UCE feature is not supported"); + return serviceProxy; + } + + TelephonyManager tm = + (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); + if (tm == null) { + Rlog.w(TAG, "create: TelephonyManager is null"); + return serviceProxy; + } + + IImsRcsFeature binder = tm.getImsRcsFeatureAndListen(slotId, serviceProxy.getListener()); + if (binder != null) { + serviceProxy.setBinder(binder.asBinder()); + // Trigger the cache to be updated for feature status. + serviceProxy.getFeatureState(); + } else { + Rlog.w(TAG, "create: binder is null! Slot Id: " + slotId); + } + + Rlog.d(TAG, "create: RcsFeatureConnection"); + return serviceProxy; + } + + private RcsFeatureConnection(Context context, int slotId) { + super(context, slotId, ImsFeature.FEATURE_RCS); + } + + /** + * Testing interface used to mock RcsFeatureManager in testing + * @hide + */ + @VisibleForTesting + public interface RcsFeatureManagerProxy { + /** + * Mock-able interface for + * {@link RcsFeatureManager#isRcsUceSupportedByCarrier(Context, int)} used for testing. + */ + boolean isRcsUceSupportedByCarrier(Context context, int slotId); + } + + private static RcsFeatureManagerProxy sRcsFeatureManagerProxy = new RcsFeatureManagerProxy() { + @Override + public boolean isRcsUceSupportedByCarrier(Context context, int slotId) { + return RcsFeatureManager.isRcsUceSupportedByCarrier(context, slotId); + } + }; + + /** + * Testing function used to mock RcsFeatureManager in testing + * @hide + */ + @VisibleForTesting + public static void setRcsFeatureManagerProxy(RcsFeatureManagerProxy proxy) { + sRcsFeatureManagerProxy = proxy; + } + + @Override + protected void handleImsFeatureCreatedCallback(int slotId, int feature) { + Log.i(TAG, "IMS feature created: slotId= " + slotId + ", feature=" + feature); + if (!isUpdateForThisFeatureAndSlot(slotId, feature)) { + return; + } + + synchronized(mLock) { + if (!mIsAvailable) { + Log.i(TAG, "RCS enabled on slotId: " + slotId); + mIsAvailable = true; + } + } + } + + @Override + protected void handleImsFeatureRemovedCallback(int slotId, int feature) { + Log.i(TAG, "IMS feature removed: slotId= " + slotId + ", feature=" + feature); + if (!isUpdateForThisFeatureAndSlot(slotId, feature)) { + return; + } + + synchronized(mLock) { + mIsAvailable = false; + } + } + + @Override + protected void handleImsStatusChangedCallback(int slotId, int feature, int status) { + Log.i(TAG, "IMS status changed: slotId=" + slotId + + ", feature=" + feature + ", status=" + status); + if (!isUpdateForThisFeatureAndSlot(slotId, feature)) { + return; + } + + synchronized(mLock) { + mFeatureStateCached = status; + } + } + + private boolean isUpdateForThisFeatureAndSlot(int slotId, int feature) { + if (mSlotId == slotId && feature == ImsFeature.FEATURE_RCS) { + return true; + } + return false; + } + + public void setRcsFeatureListener(IRcsFeatureListener listener) throws RemoteException { + checkServiceIsReady(); + getServiceInterface(mBinder).setListener(listener); + } + + @Override + @VisibleForTesting + public void checkServiceIsReady() throws RemoteException { + super.checkServiceIsReady(); + if (!sRcsFeatureManagerProxy.isRcsUceSupportedByCarrier(mContext, mSlotId)) { + throw new RemoteException("RCS UCE feature is not supported"); + } + } + + @Override + @VisibleForTesting + public Integer retrieveFeatureState() { + if (mBinder != null) { + try { + return getServiceInterface(mBinder).getFeatureState(); + } catch (RemoteException e) { + // Status check failed, don't update cache + } + } + return null; + } + + private IImsRcsFeature getServiceInterface(IBinder b) { + return IImsRcsFeature.Stub.asInterface(b); + } +} diff --git a/src/java/com/android/ims/RcsFeatureManager.java b/src/java/com/android/ims/RcsFeatureManager.java new file mode 100644 index 00000000..10cd1c7f --- /dev/null +++ b/src/java/com/android/ims/RcsFeatureManager.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.ims; + +import android.content.Context; +import android.os.PersistableBundle; +import android.telephony.CarrierConfigManager; +import android.telephony.SubscriptionManager; +import android.util.Log; + +import com.android.internal.annotations.VisibleForTesting; + +public class RcsFeatureManager { + private static final String TAG = "RcsFeatureManager"; + + private final int mSlotId; + private final Context mContext; + private RcsFeatureConnection mRcsFeatureConnection; + + public RcsFeatureManager(Context context, int slotId) { + Log.d(TAG, "RcsFeatureManager slotId: " + slotId); + mContext = context; + mSlotId = slotId; + + mRcsFeatureConnection = RcsFeatureConnection.create(mContext, mSlotId); + } + + /** + * Testing interface used to mock SubscriptionManager in testing + * @hide + */ + @VisibleForTesting + public interface SubscriptionManagerProxy { + /** + * Mock-able interface for {@link SubscriptionManager#getSubId(int)} used for testing. + */ + int getSubId(int slotId); + } + + private static SubscriptionManagerProxy sSubscriptionManagerProxy + = new SubscriptionManagerProxy() { + @Override + public int getSubId(int slotId) { + int[] subIds = SubscriptionManager.getSubId(slotId); + if (subIds != null) { + Log.i(TAG, "getSubId : " + subIds[0]); + return subIds[0]; + } + return SubscriptionManager.INVALID_SUBSCRIPTION_ID; + } + }; + + /** + * Testing function used to mock SubscriptionManager in testing + * @hide + */ + @VisibleForTesting + public static void setSubscriptionManager(SubscriptionManagerProxy proxy) { + sSubscriptionManagerProxy = proxy; + } + + /** + * Check if RCS UCE feature is supported by carrier. + */ + public static boolean isRcsUceSupportedByCarrier(Context context, int slotId) { + int subId = sSubscriptionManagerProxy.getSubId(slotId); + if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { + Log.e(TAG, "Getting subIds is failure! slotId: " + slotId); + return false; + } + + boolean isPresenceSupported = false; + boolean isSipOptionsSupported = false; + CarrierConfigManager configManager = + (CarrierConfigManager) context.getSystemService(Context.CARRIER_CONFIG_SERVICE); + if (configManager != null) { + PersistableBundle b = configManager.getConfigForSubId(subId); + if (b != null) { + isPresenceSupported = + b.getBoolean(CarrierConfigManager.KEY_USE_RCS_PRESENCE_BOOL, false); + isSipOptionsSupported = + b.getBoolean(CarrierConfigManager.KEY_USE_RCS_SIP_OPTIONS_BOOL, false); + } + } + Log.d(TAG, "isRcsUceSupportedByCarrier subId: " + subId + + ", presence= " + isPresenceSupported + + ", sip options=" + isSipOptionsSupported); + return isPresenceSupported|isSipOptionsSupported; + } +} |