aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrad Ebinger <breadley@google.com>2018-09-25 17:38:19 -0700
committerBrad Ebinger <breadley@google.com>2018-10-22 10:48:52 -0700
commit8534d931f92dc102717b81adcc45c3aeff2500d3 (patch)
tree8cf2616f95e1d6eb7f6eddded15c589f7b60408e
parent3d4b2bd5a225f9676457e1dab975fac8993f15ba (diff)
downloadims-8534d931f92dc102717b81adcc45c3aeff2500d3.tar.gz
Add ImsManager public API and refactor to use API.
Modified ImsManager to "simulate" the new ImsMmtelManager functionality until the framework can be refactored to remove ImsManager entirely. Test: atest FrameworksTelephonyTests Merged-In: Id754f07e132e641f930e638ea06770ac1e8cae57 Change-Id: Id754f07e132e641f930e638ea06770ac1e8cae57
-rw-r--r--src/java/com/android/ims/ImsConnectionStateListener.java7
-rw-r--r--src/java/com/android/ims/ImsManager.java205
-rw-r--r--src/java/com/android/ims/MmTelFeatureConnection.java326
3 files changed, 443 insertions, 95 deletions
diff --git a/src/java/com/android/ims/ImsConnectionStateListener.java b/src/java/com/android/ims/ImsConnectionStateListener.java
index 8ac473a5..8bfab8ce 100644
--- a/src/java/com/android/ims/ImsConnectionStateListener.java
+++ b/src/java/com/android/ims/ImsConnectionStateListener.java
@@ -17,6 +17,7 @@
package com.android.ims;
import android.net.Uri;
+import android.telephony.ims.ImsMmTelManager;
import android.telephony.ims.ImsReasonInfo;
import android.telephony.ims.feature.ImsFeature;
import android.telephony.ims.feature.MmTelFeature;
@@ -28,10 +29,10 @@ import java.util.Arrays;
* Listener for receiving notifications about changes to the IMS connection.
* It provides a state of IMS registration between UE and IMS network, the service
* availability of the local device during IMS registered.
- * @Deprecated Use {@link ImsRegistrationImplBase.Callback} instead.
+ * @Deprecated Use {@link ImsMmTelManager.RegistrationCallback} instead.
* @hide
*/
-public class ImsConnectionStateListener extends ImsRegistrationImplBase.Callback {
+public class ImsConnectionStateListener extends ImsMmTelManager.RegistrationCallback {
@Override
public final void onRegistered(@ImsRegistrationImplBase.ImsRegistrationTech int imsRadioTech) {
@@ -69,7 +70,7 @@ public class ImsConnectionStateListener extends ImsRegistrationImplBase.Callback
*/
public void onFeatureCapabilityChangedAdapter(
@ImsRegistrationImplBase.ImsRegistrationTech int imsRadioTech,
- ImsFeature.Capabilities c) {
+ MmTelFeature.MmTelCapabilities c) {
// Size of ImsConfig.FeatureConstants
int[] enabledCapabilities = new int[6];
// UNKNOWN means disabled.
diff --git a/src/java/com/android/ims/ImsManager.java b/src/java/com/android/ims/ImsManager.java
index 3eccfc7d..1468da27 100644
--- a/src/java/com/android/ims/ImsManager.java
+++ b/src/java/com/android/ims/ImsManager.java
@@ -21,7 +21,7 @@ import android.app.PendingIntent;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
-import android.os.IBinder;
+import android.os.HandlerExecutor;
import android.os.Looper;
import android.os.Message;
import android.os.Parcel;
@@ -31,6 +31,9 @@ import android.os.SystemProperties;
import android.provider.Settings;
import android.telecom.TelecomManager;
import android.telephony.CarrierConfigManager;
+import android.telephony.ims.ImsMmTelManager;
+import android.telephony.ims.aidl.IImsCapabilityCallback;
+import android.telephony.ims.aidl.IImsRegistrationCallback;
import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.telephony.Rlog;
import android.telephony.SubscriptionManager;
@@ -56,15 +59,18 @@ import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Set;
+import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.CopyOnWriteArraySet;
+import java.util.concurrent.Executor;
+import java.util.concurrent.LinkedBlockingDeque;
+import java.util.concurrent.TimeUnit;
/**
* Provides APIs for IMS services, such as initiating IMS calls, and provides access to
* the operator's IMS network. This class is the starting point for any IMS actions.
* You can acquire an instance of it with {@link #getInstance getInstance()}.</p>
- * <p>The APIs in this class allows you to:</p>
- *
+ * @deprecated use {@link ImsMmTelManager} instead.
* @hide
*/
public class ImsManager {
@@ -172,6 +178,8 @@ public class ImsManager {
private static final String TAG = "ImsManager";
private static final boolean DBG = true;
+ private static final int RESPONSE_WAIT_TIME_MS = 3000;
+
/**
* Helper class for managing a connection to the ImsManager when the ImsService is unavailable
* or switches to another service.
@@ -538,8 +546,7 @@ public class ImsManager {
* supported on a per slot basis.
*/
public boolean isNonTtyOrTtyOnVolteEnabled() {
- if (getBooleanCarrierConfig(
- CarrierConfigManager.KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL)) {
+ if (isTtyOnVoLteCapable()) {
return true;
}
@@ -551,6 +558,10 @@ public class ImsManager {
return tm.getCurrentTtyMode() == TelecomManager.TTY_MODE_OFF;
}
+ public boolean isTtyOnVoLteCapable() {
+ return getBooleanCarrierConfig(CarrierConfigManager.KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL);
+ }
+
/**
* Returns a platform configuration for VoLTE which may override the user setting.
* @deprecated Does not support MSIM devices. Please use
@@ -1554,8 +1565,8 @@ public class ImsManager {
* @param listener To listen to IMS registration events; It cannot be null
* @throws NullPointerException if {@code listener} is null
* @throws ImsException if calling the IMS service results in an error
- * @deprecated use {@link #addRegistrationCallback(ImsRegistrationImplBase.Callback)} and
- * {@link #addCapabilitiesCallback(ImsFeature.CapabilityCallback)} instead.
+ * @deprecated use {@link #addRegistrationCallback(ImsMmTelManager.RegistrationCallback)}
+ * instead.
*/
public void addRegistrationListener(ImsConnectionStateListener listener) throws ImsException {
if (listener == null) {
@@ -1563,29 +1574,32 @@ public class ImsManager {
}
addRegistrationCallback(listener);
// connect the ImsConnectionStateListener to the new CapabilityCallback.
- addCapabilitiesCallback(new ImsFeature.CapabilityCallback() {
+ addCapabilitiesCallback(new ImsMmTelManager.CapabilityCallback() {
@Override
- public void onCapabilitiesStatusChanged(ImsFeature.Capabilities config) {
- listener.onFeatureCapabilityChangedAdapter(getRegistrationTech(), config);
+ public void onCapabilitiesStatusChanged(
+ MmTelFeature.MmTelCapabilities capabilities) {
+ listener.onFeatureCapabilityChangedAdapter(getRegistrationTech(), capabilities);
}
});
log("Registration Callback registered.");
}
/**
- * Adds a callback that gets called when IMS registration has changed.
- * @param callback A {@link ImsRegistrationImplBase.Callback} that will notify the caller when
- * IMS registration status has changed.
+ * Adds a callback that gets called when IMS registration has changed for the slot ID
+ * associated with this ImsManager.
+ * @param callback A {@link ImsMmTelManager.RegistrationCallback} that will notify the caller
+ * when IMS registration status has changed.
* @throws ImsException when the ImsService connection is not available.
*/
- public void addRegistrationCallback(ImsRegistrationImplBase.Callback callback)
+ public void addRegistrationCallback(ImsMmTelManager.RegistrationCallback callback)
throws ImsException {
if (callback == null) {
throw new NullPointerException("registration callback can't be null");
}
try {
- mMmTelFeatureConnection.addRegistrationCallback(callback);
+ callback.setExecutor(getThreadExecutor());
+ mMmTelFeatureConnection.addRegistrationCallback(callback.getBinder());
log("Registration Callback registered.");
// Only record if there isn't a RemoteException.
} catch (RemoteException e) {
@@ -1596,33 +1610,58 @@ public class ImsManager {
/**
* Removes a previously added registration callback that was added via
- * {@link #addRegistrationCallback(ImsRegistrationImplBase.Callback)} .
- * @param callback A {@link ImsRegistrationImplBase.Callback} that was previously added.
- * @throws ImsException when the ImsService connection is not available.
+ * {@link #addRegistrationCallback(ImsMmTelManager.RegistrationCallback)} .
+ * @param callback A {@link ImsMmTelManager.RegistrationCallback} that was previously added.
*/
- public void removeRegistrationListener(ImsRegistrationImplBase.Callback callback)
- throws ImsException {
+ public void removeRegistrationListener(ImsMmTelManager.RegistrationCallback callback) {
if (callback == null) {
throw new NullPointerException("registration callback can't be null");
}
- try {
- mMmTelFeatureConnection.removeRegistrationCallback(callback);
- log("Registration callback removed.");
- } catch (RemoteException e) {
- throw new ImsException("removeRegistrationCallback(IRIB)", e,
- ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
+ mMmTelFeatureConnection.removeRegistrationCallback(callback.getBinder());
+ log("Registration callback removed.");
+ }
+
+ /**
+ * Adds a callback that gets called when IMS registration has changed for a specific
+ * subscription.
+ *
+ * @param callback A {@link ImsMmTelManager.RegistrationCallback} that will notify the caller
+ * when IMS registration status has changed.
+ * @param subId The subscription ID to register this registration callback for.
+ * @throws RemoteException when the ImsService connection is not available.
+ */
+ public void addRegistrationCallbackForSubscription(IImsRegistrationCallback callback, int subId)
+ throws RemoteException {
+ if (callback == null) {
+ throw new NullPointerException("registration callback can't be null");
+ }
+ mMmTelFeatureConnection.addRegistrationCallbackForSubscription(callback, subId);
+ log("Registration Callback registered.");
+ // Only record if there isn't a RemoteException.
+ }
+
+ /**
+ * Removes a previously registered {@link ImsMmTelManager.RegistrationCallback} callback that is
+ * associated with a specific subscription.
+ */
+ public void removeRegistrationCallbackForSubscription(IImsRegistrationCallback callback,
+ int subId) {
+ if (callback == null) {
+ throw new NullPointerException("registration callback can't be null");
}
+
+ mMmTelFeatureConnection.removeRegistrationCallbackForSubscription(callback, subId);
}
/**
* Adds a callback that gets called when MMTel capability status has changed, for example when
* Voice over IMS or VT over IMS is not available currently.
- * @param callback A {@link ImsFeature.CapabilityCallback} that will notify the caller when
- * MMTel capability status has changed.
+ * @param callback A {@link ImsMmTelManager.CapabilityCallback} that will notify the caller when
+ * MMTel capability status has changed.
* @throws ImsException when the ImsService connection is not available.
*/
- public void addCapabilitiesCallback(ImsFeature.CapabilityCallback callback)
+ public void addCapabilitiesCallback(ImsMmTelManager.CapabilityCallback callback)
throws ImsException {
if (callback == null) {
throw new NullPointerException("capabilities callback can't be null");
@@ -1630,7 +1669,8 @@ public class ImsManager {
checkAndThrowExceptionIfServiceUnavailable();
try {
- mMmTelFeatureConnection.addCapabilityCallback(callback);
+ callback.setExecutor(getThreadExecutor());
+ mMmTelFeatureConnection.addCapabilityCallback(callback.getBinder());
log("Capability Callback registered.");
// Only record if there isn't a RemoteException.
} catch (RemoteException e) {
@@ -1640,6 +1680,51 @@ public class ImsManager {
}
/**
+ * Removes a previously registered {@link ImsMmTelManager.CapabilityCallback} callback.
+ * @throws ImsException when the ImsService connection is not available.
+ */
+ public void removeCapabilitiesCallback(ImsMmTelManager.CapabilityCallback callback)
+ throws ImsException {
+ if (callback == null) {
+ throw new NullPointerException("capabilities callback can't be null");
+ }
+
+ checkAndThrowExceptionIfServiceUnavailable();
+ mMmTelFeatureConnection.removeCapabilityCallback(callback.getBinder());
+ }
+
+ /**
+ * Adds a callback that gets called when IMS capabilities have changed for a specified
+ * subscription.
+ * @param callback A {@link ImsMmTelManager.CapabilityCallback} that will notify the caller
+ * when the IMS Capabilities have changed.
+ * @param subId The subscription that is associated with the callback.
+ * @throws RemoteException when the ImsService connection is not available.
+ */
+ public void addCapabilitiesCallbackForSubscription(IImsCapabilityCallback callback, int subId)
+ throws RemoteException {
+ if (callback == null) {
+ throw new NullPointerException("registration callback can't be null");
+ }
+
+ mMmTelFeatureConnection.addCapabilityCallbackForSubscription(callback, subId);
+ log("Capability Callback registered for subscription.");
+ }
+
+ /**
+ * Removes a previously registered {@link ImsMmTelManager.CapabilityCallback} that was
+ * associated with a specific subscription.
+ */
+ public void removeCapabilitiesCallbackForSubscription(IImsCapabilityCallback callback,
+ int subId) {
+ if (callback == null) {
+ throw new NullPointerException("capabilities callback can't be null");
+ }
+
+ mMmTelFeatureConnection.removeCapabilityCallbackForSubscription(callback, subId);
+ }
+
+ /**
* Removes the registration listener from the IMS service.
*
* @param listener Previously registered listener that will be removed. Can not be null.
@@ -1654,14 +1739,9 @@ public class ImsManager {
}
checkAndThrowExceptionIfServiceUnavailable();
- try {
- mMmTelFeatureConnection.removeRegistrationCallback(listener);
- log("Registration Callback/Listener registered.");
- // Only record if there isn't a RemoteException.
- } catch (RemoteException e) {
- throw new ImsException("addRegistrationCallback()", e,
- ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
- }
+ mMmTelFeatureConnection.removeRegistrationCallback(listener.getBinder());
+ log("Registration Callback/Listener registered.");
+ // Only record if there isn't a RemoteException.
}
public @ImsRegistrationImplBase.ImsRegistrationTech int getRegistrationTech() {
@@ -1899,6 +1979,48 @@ public class ImsManager {
thread.start();
}
+ public boolean queryMmTelCapability(
+ @MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
+ @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) throws ImsException {
+ checkAndThrowExceptionIfServiceUnavailable();
+
+ BlockingQueue<Boolean> result = new LinkedBlockingDeque<>(1);
+
+ try {
+ mMmTelFeatureConnection.queryEnabledCapabilities(capability, radioTech,
+ new IImsCapabilityCallback.Stub() {
+ @Override
+ public void onQueryCapabilityConfiguration(int resCap, int resTech,
+ boolean enabled) {
+ if (resCap == capability && resTech == radioTech) {
+ result.offer(enabled);
+ }
+ }
+
+ @Override
+ public void onChangeCapabilityConfigurationError(int capability,
+ int radioTech, int reason) {
+
+ }
+
+ @Override
+ public void onCapabilitiesStatusChanged(int config) {
+
+ }
+ });
+ } catch (RemoteException e) {
+ throw new ImsException("queryMmTelCapability()", e,
+ ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
+ }
+
+ try {
+ return result.poll(RESPONSE_WAIT_TIME_MS, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ Log.w(TAG, "queryMmTelCapability: interrupted while waiting for response");
+ }
+ return false;
+ }
+
public void setRttEnabled(boolean enabled) {
try {
if (enabled) {
@@ -1979,6 +2101,13 @@ public class ImsManager {
return mMmTelFeatureConnection.getFeatureState();
}
+ private Executor getThreadExecutor() {
+ if (Looper.myLooper() == null) {
+ Looper.prepare();
+ }
+ return new HandlerExecutor(new Handler(Looper.myLooper()));
+ }
+
/**
* Get the boolean config from carrier config manager.
*
diff --git a/src/java/com/android/ims/MmTelFeatureConnection.java b/src/java/com/android/ims/MmTelFeatureConnection.java
index 96da3f90..16c4a493 100644
--- a/src/java/com/android/ims/MmTelFeatureConnection.java
+++ b/src/java/com/android/ims/MmTelFeatureConnection.java
@@ -20,28 +20,42 @@ import android.annotation.Nullable;
import android.content.Context;
import android.os.IBinder;
import android.os.IInterface;
+import android.os.Looper;
import android.os.Message;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.telephony.Rlog;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.telephony.ims.ImsCallProfile;
+import android.telephony.ims.aidl.IImsCapabilityCallback;
import android.telephony.ims.aidl.IImsConfig;
import android.telephony.ims.aidl.IImsMmTelFeature;
import android.telephony.ims.aidl.IImsRegistration;
+import android.telephony.ims.aidl.IImsRegistrationCallback;
import android.telephony.ims.aidl.IImsSmsListener;
import android.telephony.ims.feature.CapabilityChangeRequest;
import android.telephony.ims.feature.ImsFeature;
import android.telephony.ims.feature.MmTelFeature;
import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.telephony.ims.stub.ImsSmsImplBase;
+import android.util.ArraySet;
import android.util.Log;
+import android.util.SparseArray;
import com.android.ims.internal.IImsCallSession;
import com.android.ims.internal.IImsEcbm;
import com.android.ims.internal.IImsMultiEndpoint;
import com.android.ims.internal.IImsServiceFeatureCallback;
import com.android.ims.internal.IImsUt;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
/**
* A container of the IImsServiceController binder, which implements all of the ImsFeatures that
@@ -50,40 +64,71 @@ import com.android.ims.internal.IImsUt;
*/
public class MmTelFeatureConnection {
-
protected static final String TAG = "MmTelFeatureConnection";
- protected final int mSlotId;
- protected IBinder mBinder;
- private Context mContext;
- private volatile boolean mIsAvailable = false;
- // 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;
-
- // 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 IBinder.DeathRecipient mDeathRecipient = () -> {
- Log.w(TAG, "DeathRecipient triggered, binder died.");
- onRemovedOrDied();
- };
-
- private abstract class CallbackAdapterManager<T extends IInterface> {
+ // Manages callbacks to the associated MmTelFeature in mMmTelFeatureConnection.
+ @VisibleForTesting
+ public static abstract class CallbackAdapterManager<T extends IInterface> {
private static final String TAG = "CallbackAdapterManager";
- protected final RemoteCallbackList<T> mRemoteCallbacks =
- new RemoteCallbackList<>();
+ private final Context mContext;
+ private final Object mLock;
+ // Map of sub id -> List<callbacks> for sub id linked callbacks.
+ private final SparseArray<Set<T>> mCallbackSubscriptionMap = new SparseArray<>();
+ // List of all active callbacks to ImsService
+ private final RemoteCallbackList<T> mRemoteCallbacks = new RemoteCallbackList<>();
+ @VisibleForTesting
+ public SubscriptionManager.OnSubscriptionsChangedListener mSubChangedListener;
+
+ public CallbackAdapterManager(Context context, Object lock) {
+ mContext = context;
+ mLock = lock;
+ if (Looper.myLooper() == null) {
+ Looper.prepare();
+ }
+ // Must be created after Looper.prepare() is called, or else we will get an exception.
+ mSubChangedListener = new SubscriptionManager.OnSubscriptionsChangedListener() {
+ @Override
+ public void onSubscriptionsChanged() {
+ SubscriptionManager manager = mContext.getSystemService(
+ SubscriptionManager.class);
+ if (manager == null) {
+ Log.w(TAG, "onSubscriptionsChanged: could not find SubscriptionManager.");
+ return;
+ }
+ List<SubscriptionInfo> subInfos = manager.getActiveSubscriptionInfoList();
+ if (subInfos == null) {
+ subInfos = Collections.emptyList();
+ }
+ Set<Integer> newSubIds = subInfos.stream()
+ .map(SubscriptionInfo::getSubscriptionId)
+ .collect(Collectors.toSet());
+ synchronized (mLock) {
+ Set<Integer> storedSubIds = new ArraySet<>(mCallbackSubscriptionMap.size());
+ for (int keyIndex = 0; keyIndex < mCallbackSubscriptionMap.size();
+ keyIndex++) {
+ storedSubIds.add(mCallbackSubscriptionMap.keyAt(keyIndex));
+ }
+ // Get the set of sub ids that are in storedSubIds that are not in newSubIds.
+ // This is the set of sub ids that need to be removed.
+ storedSubIds.removeAll(newSubIds);
+ for (Integer subId : storedSubIds) {
+ removeCallbacksForSubscription(subId);
+ }
+ }
+ }
+ };
- public void addCallback(T localCallback) throws RemoteException {
+ }
+
+ // Add a callback to the MmTelFeature associated with this manager (independent of the)
+ // current subscription.
+ public final void addCallback(T localCallback) throws RemoteException {
synchronized (mLock) {
- // throws a RemoteException if a connection can not be established.
- if (!createConnection(localCallback)) {
+ // Skip registering to callback subscription map here, because we are registering
+ // for the slot, independent of subscription (deprecated behavior).
+ if (!registerCallback(localCallback)) {
+ // throws a RemoteException if a connection can not be established.
throw new RemoteException("Can not create connection!");
}
Log.i(TAG, "Local callback added: " + localCallback);
@@ -91,37 +136,163 @@ public class MmTelFeatureConnection {
}
}
- public void removeCallback(T localCallback) {
+ // Add a callback to be associated with a subscription. If that subscription is removed,
+ // remove the callback and notify the callback that the subscription has been removed.
+ public final void addCallbackForSubscription(T localCallback, int subId)
+ throws RemoteException {
+ if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+ return;
+ }
+ synchronized (mLock) {
+ addCallback(localCallback);
+ linkCallbackToSubscription(localCallback, subId);
+ }
+ }
+
+ // Removes a callback associated with the MmTelFeature.
+ public final void removeCallback(T localCallback) {
Log.i(TAG, "Local callback removed: " + localCallback);
synchronized (mLock) {
- removeConnection(localCallback);
- mRemoteCallbacks.unregister(localCallback);
+ if (mRemoteCallbacks.unregister(localCallback)) {
+ // Will only occur if we have record of this callback in mRemoteCallbacks.
+ unregisterCallback(localCallback);
+ }
+ }
+ }
+
+ // Remove an existing callback that has been linked to a subscription.
+ public final void removeCallbackForSubscription(T localCallback, int subId) {
+ if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+ return;
+ }
+ synchronized (mLock) {
+ removeCallback(localCallback);
+ unlinkCallbackFromSubscription(localCallback, subId);
+ }
+ }
+
+ // Links a callback to be tracked by a subscription. If it goes away, emove.
+ private void linkCallbackToSubscription(T callback, int subId) {
+ synchronized (mLock) {
+ if (mCallbackSubscriptionMap.size() == 0) {
+ // we are about to add the first entry to the map, register for subscriptions
+ //changed listener.
+ registerForSubscriptionsChanged();
+ }
+ Set<T> callbacksPerSub = mCallbackSubscriptionMap.get(subId);
+ if (callbacksPerSub == null) {
+ // the callback list has not been created yet for this subscription.
+ callbacksPerSub = new ArraySet<>();
+ mCallbackSubscriptionMap.put(subId, callbacksPerSub);
+ }
+ callbacksPerSub.add(callback);
+ }
+ }
+
+ // Unlink the callback from the associated subscription.
+ private void unlinkCallbackFromSubscription(T callback, int subId) {
+ synchronized (mLock) {
+ Set<T> callbacksPerSub = mCallbackSubscriptionMap.get(subId);
+ if (callbacksPerSub != null) {
+ callbacksPerSub.remove(callback);
+ if (callbacksPerSub.isEmpty()) {
+ mCallbackSubscriptionMap.remove(subId);
+ }
+ }
+ if (mCallbackSubscriptionMap.size() == 0) {
+ unregisterForSubscriptionsChanged();
+ }
}
}
+ // Removes all of the callbacks that have been registered to the subscription specified.
+ // This happens when Telephony sends an indication that the subscriptions have changed.
+ private void removeCallbacksForSubscription(int subId) {
+ if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+ return;
+ }
+ synchronized (mLock) {
+ Set<T> callbacksPerSub = mCallbackSubscriptionMap.get(subId);
+ if (callbacksPerSub == null) {
+ // no callbacks registered for this subscription.
+ return;
+ }
+ // clear all registered callbacks in the subscription map for this subscription.
+ mCallbackSubscriptionMap.remove(subId);
+ for (T callback : callbacksPerSub) {
+ removeCallback(callback);
+ }
+ // If there are no more callbacks being tracked, remove subscriptions changed
+ // listener.
+ if (mCallbackSubscriptionMap.size() == 0) {
+ unregisterForSubscriptionsChanged();
+ }
+ }
+ }
+
+ // Clear the Subscription -> Callback map because the ImsService connection is no longer
+ // current.
+ private void clearCallbacksForAllSubscriptions() {
+ synchronized (mLock) {
+ List<Integer> keys = new ArrayList<>();
+ for (int keyIndex = 0; keyIndex < mCallbackSubscriptionMap.size(); keyIndex++) {
+ keys.add(mCallbackSubscriptionMap.keyAt(keyIndex));
+ }
+ keys.forEach(this::removeCallbacksForSubscription);
+ }
+ }
+
+ private void registerForSubscriptionsChanged() {
+ SubscriptionManager manager = mContext.getSystemService(SubscriptionManager.class);
+ if (manager != null) {
+ manager.addOnSubscriptionsChangedListener(mSubChangedListener);
+ } else {
+ Log.w(TAG, "registerForSubscriptionsChanged: could not find SubscriptionManager.");
+ }
+ }
+
+ private void unregisterForSubscriptionsChanged() {
+ SubscriptionManager manager = mContext.getSystemService(SubscriptionManager.class);
+ if (manager != null) {
+ manager.removeOnSubscriptionsChangedListener(mSubChangedListener);
+ } else {
+ Log.w(TAG, "unregisterForSubscriptionsChanged: could not find"
+ + " SubscriptionManager.");
+ }
+ }
+
+ // The ImsService these callbacks are registered to has become unavailable or crashed, or
+ // the ImsResolver has switched to a new ImsService. In these cases, clean up all existing
+ // callbacks.
public void close() {
synchronized (mLock) {
final int lastCallbackIndex = mRemoteCallbacks.getRegisteredCallbackCount() - 1;
for(int ii = lastCallbackIndex; ii >= 0; ii --) {
T callbackItem = mRemoteCallbacks.getRegisteredCallbackItem(ii);
- removeConnection(callbackItem);
+ unregisterCallback(callbackItem);
mRemoteCallbacks.unregister(callbackItem);
}
+ clearCallbacksForAllSubscriptions();
Log.i(TAG, "Closing connection and clearing callbacks");
}
}
- abstract boolean createConnection(T localCallback) throws RemoteException;
+ // A callback has been registered. Register that callback with the MmTelFeature.
+ public abstract boolean registerCallback(T localCallback) throws RemoteException;
- abstract void removeConnection(T localCallback);
+ // A callback has been removed, unregister that callback with the MmTelFeature.
+ public abstract void unregisterCallback(T localCallback);
}
- private ImsRegistrationCallbackAdapter mRegistrationCallbackManager
- = new ImsRegistrationCallbackAdapter();
- private class ImsRegistrationCallbackAdapter
- extends CallbackAdapterManager<ImsRegistrationImplBase.Callback> {
+
+ private class ImsRegistrationCallbackAdapter extends
+ CallbackAdapterManager<IImsRegistrationCallback> {
+
+ public ImsRegistrationCallbackAdapter(Context context, Object lock) {
+ super(context, lock);
+ }
@Override
- boolean createConnection(ImsRegistrationImplBase.Callback localCallback)
+ public boolean registerCallback(IImsRegistrationCallback localCallback)
throws RemoteException {
IImsRegistration imsRegistration = getRegistration();
if (imsRegistration != null) {
@@ -134,13 +305,13 @@ public class MmTelFeatureConnection {
}
@Override
- void removeConnection(ImsRegistrationImplBase.Callback localCallback) {
+ public void unregisterCallback(IImsRegistrationCallback localCallback) {
IImsRegistration imsRegistration = getRegistration();
if (imsRegistration != null) {
try {
getRegistration().removeRegistrationCallback(localCallback);
} catch (RemoteException e) {
- Log.w(TAG, "removeConnection: couldn't remove registration callback");
+ Log.w(TAG, "unregisterCallback: couldn't remove registration callback");
}
} else {
Log.e(TAG, "ImsRegistration is null");
@@ -148,13 +319,15 @@ public class MmTelFeatureConnection {
}
}
- private final CapabilityCallbackManager mCapabilityCallbackManager
- = new CapabilityCallbackManager();
- private class CapabilityCallbackManager
- extends CallbackAdapterManager<ImsFeature.CapabilityCallback> {
+ private class CapabilityCallbackManager extends CallbackAdapterManager<IImsCapabilityCallback> {
+
+ public CapabilityCallbackManager(Context context, Object lock) {
+ super(context, lock);
+ }
@Override
- boolean createConnection(ImsFeature.CapabilityCallback localCallback) throws RemoteException {
+ public boolean registerCallback(IImsCapabilityCallback localCallback)
+ throws RemoteException {
IImsMmTelFeature binder;
synchronized (mLock) {
checkServiceIsReady();
@@ -170,7 +343,7 @@ public class MmTelFeatureConnection {
}
@Override
- void removeConnection(ImsFeature.CapabilityCallback localCallback) {
+ public void unregisterCallback(IImsCapabilityCallback localCallback) {
IImsMmTelFeature binder = null;
synchronized (mLock) {
try {
@@ -193,6 +366,31 @@ public class MmTelFeatureConnection {
}
}
+ protected final int mSlotId;
+ protected IBinder mBinder;
+ private Context mContext;
+
+ private volatile boolean mIsAvailable = false;
+ // 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;
+
+ // 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 IBinder.DeathRecipient mDeathRecipient = () -> {
+ Log.w(TAG, "DeathRecipient triggered, binder died.");
+ onRemovedOrDied();
+ };
+
+ private final ImsRegistrationCallbackAdapter mRegistrationCallbackManager;
+ private final CapabilityCallbackManager mCapabilityCallbackManager;
+
public static MmTelFeatureConnection create(Context context , int slotId) {
MmTelFeatureConnection serviceProxy = new MmTelFeatureConnection(context, slotId);
@@ -303,6 +501,8 @@ public class MmTelFeatureConnection {
public MmTelFeatureConnection(Context context, int slotId) {
mSlotId = slotId;
mContext = context;
+ mRegistrationCallbackManager = new ImsRegistrationCallbackAdapter(context, mLock);
+ mCapabilityCallbackManager = new CapabilityCallbackManager(context, mLock);
}
/**
@@ -416,28 +616,46 @@ public class MmTelFeatureConnection {
}
}
- public void addRegistrationCallback(ImsRegistrationImplBase.Callback callback)
+ public void addRegistrationCallback(IImsRegistrationCallback callback)
throws RemoteException {
mRegistrationCallbackManager.addCallback(callback);
}
- public void removeRegistrationCallback(ImsRegistrationImplBase.Callback callback)
+ public void addRegistrationCallbackForSubscription(IImsRegistrationCallback callback, int subId)
throws RemoteException {
+ mRegistrationCallbackManager.addCallbackForSubscription(callback , subId);
+ }
+
+ public void removeRegistrationCallback(IImsRegistrationCallback callback) {
mRegistrationCallbackManager.removeCallback(callback);
}
- public void addCapabilityCallback(ImsFeature.CapabilityCallback callback)
+ public void removeRegistrationCallbackForSubscription(IImsRegistrationCallback callback,
+ int subId) {
+ mRegistrationCallbackManager.removeCallbackForSubscription(callback, subId);
+ }
+
+ public void addCapabilityCallback(IImsCapabilityCallback callback)
throws RemoteException {
mCapabilityCallbackManager.addCallback(callback);
}
- public void removeCapabilityCallback(ImsFeature.CapabilityCallback callback)
- throws RemoteException {
+ public void addCapabilityCallbackForSubscription(IImsCapabilityCallback callback,
+ int subId) throws RemoteException {
+ mCapabilityCallbackManager.addCallbackForSubscription(callback, subId);
+ }
+
+ public void removeCapabilityCallback(IImsCapabilityCallback callback) {
mCapabilityCallbackManager.removeCallback(callback);
}
+ public void removeCapabilityCallbackForSubscription(IImsCapabilityCallback callback,
+ int subId) {
+ mCapabilityCallbackManager.removeCallbackForSubscription(callback , subId);
+ }
+
public void changeEnabledCapabilities(CapabilityChangeRequest request,
- ImsFeature.CapabilityCallback callback) throws RemoteException {
+ IImsCapabilityCallback callback) throws RemoteException {
synchronized (mLock) {
checkServiceIsReady();
getServiceInterface(mBinder).changeCapabilitiesConfiguration(request, callback);
@@ -445,7 +663,7 @@ public class MmTelFeatureConnection {
}
public void queryEnabledCapabilities(int capability, int radioTech,
- ImsFeature.CapabilityCallback callback) throws RemoteException {
+ IImsCapabilityCallback callback) throws RemoteException {
synchronized (mLock) {
checkServiceIsReady();
getServiceInterface(mBinder).queryCapabilityConfiguration(capability, radioTech,