diff options
author | Brad Ebinger <breadley@google.com> | 2018-09-25 17:38:19 -0700 |
---|---|---|
committer | Brad Ebinger <breadley@google.com> | 2018-10-22 10:48:52 -0700 |
commit | 8534d931f92dc102717b81adcc45c3aeff2500d3 (patch) | |
tree | 8cf2616f95e1d6eb7f6eddded15c589f7b60408e | |
parent | 3d4b2bd5a225f9676457e1dab975fac8993f15ba (diff) | |
download | ims-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.java | 7 | ||||
-rw-r--r-- | src/java/com/android/ims/ImsManager.java | 205 | ||||
-rw-r--r-- | src/java/com/android/ims/MmTelFeatureConnection.java | 326 |
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, |