aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJames.cf Lin <jamescflin@google.com>2019-10-01 14:51:33 +0800
committerJames Lin <jamescflin@google.com>2019-10-01 19:32:22 +0000
commit79e54d9407c76d260a4e8bf7175f99a3339be673 (patch)
treef956687bec5f2f5014d5da1c01984d9cd215a8b3
parent8d6c18b5e9642dac8778e7f323375ab74ba10af4 (diff)
downloadims-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.java210
-rw-r--r--src/java/com/android/ims/MmTelFeatureConnection.java262
-rw-r--r--src/java/com/android/ims/RcsFeatureConnection.java183
-rw-r--r--src/java/com/android/ims/RcsFeatureManager.java104
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;
+ }
+}