Add the ability to remove FeatureConnection callbacks

FeatureConnection status callbacks were not being cleaned up and
were staying around indefinitely, even after the FeatureConnection
was destroyed.

Bug: 156893040
Test: atest FrameworksTelephonyTests
Change-Id: I28dc51b10dc230bc8f7a03b7373510cf3a29e93a
diff --git a/src/java/com/android/internal/telephony/ims/ImsResolver.java b/src/java/com/android/internal/telephony/ims/ImsResolver.java
index 50b0954..2d293a1 100644
--- a/src/java/com/android/internal/telephony/ims/ImsResolver.java
+++ b/src/java/com/android/internal/telephony/ims/ImsResolver.java
@@ -767,6 +767,22 @@
         return null;
     }
 
+    /**
+     * Unregister a previously registered IImsServiceFeatureCallback through
+     * {@link #getImsServiceControllerAndListen(int, int, IImsServiceFeatureCallback)} .
+     * @param slotId The slot id associated with the ImsFeature.
+     * @param feature The {@link ImsFeature.FeatureType}
+     * @param callback The callback to be unregistered.
+     */
+    public void unregisterImsFeatureCallback(int slotId, int feature,
+            IImsServiceFeatureCallback callback) {
+        ImsServiceController controller = getImsServiceController(slotId, feature);
+
+        if (controller != null) {
+            controller.removeImsServiceFeatureCallback(callback);
+        }
+    }
+
     // Used for testing only.
     public boolean overrideImsServiceConfiguration(int slotId, boolean isCarrierService,
             Map<Integer, String> featureConfig) {
diff --git a/src/java/com/android/internal/telephony/ims/ImsServiceController.java b/src/java/com/android/internal/telephony/ims/ImsServiceController.java
index 52fc666..bc5ddbe 100644
--- a/src/java/com/android/internal/telephony/ims/ImsServiceController.java
+++ b/src/java/com/android/internal/telephony/ims/ImsServiceController.java
@@ -504,19 +504,28 @@
      */
     public void addImsServiceFeatureCallback(IImsServiceFeatureCallback callback) {
         mImsStatusCallbacks.add(callback);
+        Set<ImsFeatureConfiguration.FeatureSlotPair> features;
         synchronized (mLock) {
             if (mImsFeatures == null || mImsFeatures.isEmpty()) {
                 return;
             }
-            // notify the new status callback of the features that are available.
-            try {
-                for (ImsFeatureConfiguration.FeatureSlotPair i : mImsFeatures) {
-                    callback.imsFeatureCreated(i.slotId, i.featureType);
-                }
-            } catch (RemoteException e) {
-                Log.w(LOG_TAG, "addImsServiceFeatureCallback: exception notifying callback");
-            }
+            features = new HashSet<>(mImsFeatures);
         }
+        // notify the new status callback of the features that are available.
+        try {
+            for (ImsFeatureConfiguration.FeatureSlotPair i : features) {
+                callback.imsFeatureCreated(i.slotId, i.featureType);
+            }
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "addImsServiceFeatureCallback: exception notifying callback");
+        }
+    }
+
+    /**
+     * Removes a previously registered callback if it was associated with this feature.
+     */
+    public void removeImsServiceFeatureCallback(IImsServiceFeatureCallback callback) {
+        mImsStatusCallbacks.remove(callback);
     }
 
     public void enableIms(int slotId) {
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ims/FeatureConnectionTest.java b/tests/telephonytests/src/com/android/internal/telephony/ims/FeatureConnectionTest.java
index 267f1f9..bac45ef 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ims/FeatureConnectionTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ims/FeatureConnectionTest.java
@@ -28,7 +28,9 @@
 import android.content.pm.PackageManager;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.telephony.ims.aidl.IImsRegistration;
 import android.telephony.ims.feature.ImsFeature;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.ims.FeatureConnection;
@@ -59,8 +61,8 @@
         public boolean isFeatureRemovedCalled = false;
         public int mNewStatus = ImsFeature.STATE_UNAVAILABLE;
 
-        TestFeatureConnection(Context context, int slotId, int featureType) {
-            super(context, slotId, featureType);
+        TestFeatureConnection(Context context, int slotId) {
+            super(context, slotId);
             if (!ImsManager.isImsSupportedOnDevice(context)) {
                 sImsSupportedOnDevice = false;
             }
@@ -91,6 +93,11 @@
             return mFeatureState;
         }
 
+        @Override
+        protected IImsRegistration getRegistrationBinder() {
+            return getTestRegistrationBinder();
+        }
+
         public void setFeatureState(int state) {
             mFeatureState = state;
         }
@@ -99,6 +106,7 @@
     private int mPhoneId;
     private TestFeatureConnection mTestFeatureConnection;
     @Mock IBinder mBinder;
+    @Mock IImsRegistration mRegistrationBinder;
 
     @Before
     public void setUp() throws Exception {
@@ -108,8 +116,7 @@
         doReturn(null).when(mContext).getMainLooper();
         doReturn(true).when(mPackageManager).hasSystemFeature(PackageManager.FEATURE_TELEPHONY_IMS);
 
-        mTestFeatureConnection = new TestFeatureConnection(
-                mContext, mPhoneId, ImsFeature.FEATURE_RCS);
+        mTestFeatureConnection = new TestFeatureConnection(mContext, mPhoneId);
         mTestFeatureConnection.mExecutor = mSimpleExecutor;
         mTestFeatureConnection.setBinder(mBinder);
     }
@@ -164,6 +171,20 @@
     }
 
     /**
+     * Test registration tech callbacks.
+     */
+    @Test
+    @SmallTest
+    public void testRegistrationTech() throws Exception {
+        when(mRegistrationBinder.getRegistrationTechnology()).thenReturn(
+                ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN);
+
+        assertEquals(ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN,
+                mTestFeatureConnection.getRegistrationTech());
+
+    }
+
+    /**
      * Test callback is called when IMS feature created/removed/changed.
      */
     @Test
@@ -192,4 +213,8 @@
             throw new AssertionFailedError("testListenerCallback(Changed): " + e);
         }
     }
+
+    private IImsRegistration getTestRegistrationBinder() {
+        return mRegistrationBinder;
+    }
 }