Move IMS to a listener type model instead of a poll model

Pipe through new listen commands for components requiring
access to ImsFeature binders and refactor the RCS
controllers to use the new model.

Test: atest TeleServiceTests
Change-Id: Ie3982c24525e5ac03d7166126694c41c37bc1ef6
diff --git a/src/com/android/phone/ImsRcsController.java b/src/com/android/phone/ImsRcsController.java
index f5f24d3..1d33514 100644
--- a/src/com/android/phone/ImsRcsController.java
+++ b/src/com/android/phone/ImsRcsController.java
@@ -29,14 +29,17 @@
 import android.telephony.ims.aidl.IImsRegistrationCallback;
 import android.telephony.ims.aidl.IRcsUceControllerCallback;
 import android.telephony.ims.aidl.IRcsUcePublishStateCallback;
+import android.telephony.ims.feature.ImsFeature;
 import android.telephony.ims.feature.RcsFeature;
 import android.telephony.ims.stub.ImsRegistrationImplBase;
 import android.util.Log;
 
 import com.android.ims.ImsManager;
+import com.android.ims.internal.IImsServiceFeatureCallback;
 import com.android.internal.telephony.IIntegerConsumer;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.TelephonyPermissions;
+import com.android.internal.telephony.ims.ImsResolver;
 import com.android.internal.telephony.imsphone.ImsPhone;
 import com.android.services.telephony.rcs.RcsFeatureController;
 import com.android.services.telephony.rcs.TelephonyRcsService;
@@ -55,6 +58,7 @@
 
     private PhoneGlobals mApp;
     private TelephonyRcsService mRcsService;
+    private ImsResolver mImsResolver;
 
     /**
      * Initialize the singleton ImsRcsController instance.
@@ -77,6 +81,7 @@
         mApp = app;
         TelephonyFrameworkInitializer
                 .getTelephonyServiceManager().getTelephonyImsServiceRegisterer().register(this);
+        mImsResolver = mApp.getImsResolver();
     }
 
     /**
@@ -349,6 +354,46 @@
     }
 
     /**
+     * Registers for updates to the RcsFeature connection through the IImsServiceFeatureCallback
+     * callback.
+     */
+    @Override
+    public void registerRcsFeatureCallback(int slotId, IImsServiceFeatureCallback callback,
+            boolean oneShot) {
+        enforceModifyPermission();
+
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            if (mImsResolver == null) {
+                throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
+                        "Device does not support IMS");
+            }
+            if (oneShot) {
+                mImsResolver.callBackIfExists(slotId, ImsFeature.FEATURE_RCS, callback);
+            } else {
+                mImsResolver.listenForFeature(slotId, ImsFeature.FEATURE_RCS, callback);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+    /**
+     * Unregister a previously registered IImsServiceFeatureCallback associated with an ImsFeature.
+     */
+    @Override
+    public void unregisterImsFeatureCallback(IImsServiceFeatureCallback callback) {
+        enforceModifyPermission();
+
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            if (mImsResolver == null) return;
+            mImsResolver.unregisterImsFeatureCallback(callback);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    /**
      * Make sure either called from same process as self (phone) or IPC caller has read privilege.
      *
      * @throws SecurityException if the caller does not have the required permission
diff --git a/src/com/android/phone/PhoneGlobals.java b/src/com/android/phone/PhoneGlobals.java
index bd2b2ed..509aa57 100644
--- a/src/com/android/phone/PhoneGlobals.java
+++ b/src/com/android/phone/PhoneGlobals.java
@@ -55,6 +55,7 @@
 import android.util.Log;
 import android.widget.Toast;
 
+import com.android.ims.ImsFeatureBinderRepository;
 import com.android.internal.telephony.CallManager;
 import com.android.internal.telephony.IccCardConstants;
 import com.android.internal.telephony.MmiCode;
@@ -356,7 +357,8 @@
                 String defaultImsRcsPackage = getResources().getString(
                         R.string.config_ims_rcs_package);
                 mImsResolver = new ImsResolver(this, defaultImsMmtelPackage,
-                        defaultImsRcsPackage, PhoneFactory.getPhones().length);
+                        defaultImsRcsPackage, PhoneFactory.getPhones().length,
+                        new ImsFeatureBinderRepository());
                 mImsResolver.initialize();
             }
 
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index c22a8a4..9ddbe47 100755
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -104,8 +104,6 @@
 import android.telephony.ims.aidl.IImsCapabilityCallback;
 import android.telephony.ims.aidl.IImsConfig;
 import android.telephony.ims.aidl.IImsConfigCallback;
-import android.telephony.ims.aidl.IImsMmTelFeature;
-import android.telephony.ims.aidl.IImsRcsFeature;
 import android.telephony.ims.aidl.IImsRegistration;
 import android.telephony.ims.aidl.IImsRegistrationCallback;
 import android.telephony.ims.feature.ImsFeature;
@@ -5007,58 +5005,40 @@
     }
 
     /**
-     * Returns the {@link IImsMmTelFeature} that corresponds to the given slot Id for the MMTel
-     * feature or {@link null} if the service is not available. If the feature is available, the
-     * {@link IImsServiceFeatureCallback} callback is registered as a listener for feature updates.
+     * Registers for updates to the MmTelFeature connection through the IImsServiceFeatureCallback
+     * callback.
      */
-    public IImsMmTelFeature getMmTelFeatureAndListen(int slotId,
-            IImsServiceFeatureCallback callback) {
+    @Override
+    public void registerMmTelFeatureCallback(int slotId, IImsServiceFeatureCallback callback,
+            boolean oneShot) {
         enforceModifyPermission();
 
         final long identity = Binder.clearCallingIdentity();
         try {
             if (mImsResolver == null) {
-                // may happen if the device does not support IMS.
-                return null;
+                throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
+                        "Device does not support IMS");
             }
-            return mImsResolver.getMmTelFeatureAndListen(slotId, callback);
+            if (oneShot) {
+                mImsResolver.callBackIfExists(slotId, ImsFeature.FEATURE_MMTEL, callback);
+            } else {
+                mImsResolver.listenForFeature(slotId, ImsFeature.FEATURE_MMTEL, callback);
+            }
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
     }
-
-    /**
-     * Returns the {@link IImsRcsFeature} that corresponds to the given slot Id for the RCS
-     * feature during emergency calling or {@link null} if the service is not available. If the
-     * feature is available, the {@link IImsServiceFeatureCallback} callback is registered as a
-     * listener for feature updates.
-     */
-    public IImsRcsFeature getRcsFeatureAndListen(int slotId, IImsServiceFeatureCallback callback) {
-        enforceModifyPermission();
-
-        final long identity = Binder.clearCallingIdentity();
-        try {
-            if (mImsResolver == null) {
-                // may happen if the device does not support IMS.
-                return null;
-            }
-            return mImsResolver.getRcsFeatureAndListen(slotId, callback);
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
-    }
-
     /**
      * Unregister a previously registered IImsServiceFeatureCallback associated with an ImsFeature.
      */
-    public void unregisterImsFeatureCallback(int slotId, int featureType,
-            IImsServiceFeatureCallback callback) {
+    @Override
+    public void unregisterImsFeatureCallback(IImsServiceFeatureCallback callback) {
         enforceModifyPermission();
 
         final long identity = Binder.clearCallingIdentity();
         try {
             if (mImsResolver == null) return;
-            mImsResolver.unregisterImsFeatureCallback(slotId, featureType, callback);
+            mImsResolver.unregisterImsFeatureCallback(callback);
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
diff --git a/src/com/android/services/telephony/TelephonyConnection.java b/src/com/android/services/telephony/TelephonyConnection.java
index 5a2b384..916b4ce 100755
--- a/src/com/android/services/telephony/TelephonyConnection.java
+++ b/src/com/android/services/telephony/TelephonyConnection.java
@@ -1586,7 +1586,7 @@
                 }
             }
 
-            isVowifiEnabled = ImsUtil.isWfcEnabled(phone.getContext(), phone.getPhoneId());
+            isVowifiEnabled = isWfcEnabled(phone);
         }
 
         if (isCurrentVideoCall) {
@@ -2722,7 +2722,7 @@
         boolean isIms = phone.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS;
         boolean isVoWifiEnabled = false;
         if (isIms) {
-            isVoWifiEnabled = ImsUtil.isWfcEnabled(phone.getContext(), phone.getPhoneId());
+            isVoWifiEnabled = isWfcEnabled(phone);
         }
         boolean isRttMergeSupported = getCarrierConfig()
                 .getBoolean(CarrierConfigManager.KEY_ALLOW_MERGING_RTT_CALLS_BOOL);
@@ -2772,6 +2772,12 @@
             notifyConferenceSupportedChanged(isConferenceSupported);
         }
     }
+
+    @VisibleForTesting
+    boolean isWfcEnabled(Phone phone) {
+        return ImsUtil.isWfcEnabled(phone.getContext(), phone.getPhoneId());
+    }
+
     /**
      * Provides a mapping from extras keys which may be found in the
      * {@link com.android.internal.telephony.Connection} to their equivalents defined in
diff --git a/src/com/android/services/telephony/rcs/RcsFeatureController.java b/src/com/android/services/telephony/rcs/RcsFeatureController.java
index fcfe312..8c6fce0 100644
--- a/src/com/android/services/telephony/rcs/RcsFeatureController.java
+++ b/src/com/android/services/telephony/rcs/RcsFeatureController.java
@@ -20,7 +20,9 @@
 import android.content.Context;
 import android.net.Uri;
 import android.telephony.ims.ImsException;
+import android.telephony.ims.ImsRcsManager;
 import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.RegistrationManager;
 import android.telephony.ims.aidl.IImsCapabilityCallback;
 import android.telephony.ims.aidl.IImsRegistrationCallback;
 import android.telephony.ims.stub.ImsRegistrationImplBase;
@@ -28,7 +30,7 @@
 import android.util.Log;
 
 import com.android.ims.FeatureConnector;
-import com.android.ims.IFeatureConnector;
+import com.android.ims.FeatureUpdates;
 import com.android.ims.RcsFeatureManager;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.imsphone.ImsRegistrationCallbackHelper;
@@ -78,13 +80,13 @@
      * Used to inject FeatureConnector instances for testing.
      */
     @VisibleForTesting
-    public interface FeatureConnectorFactory<T extends IFeatureConnector> {
+    public interface FeatureConnectorFactory<U extends FeatureUpdates> {
         /**
-         * @return a {@link FeatureConnector} associated for the given {@link IFeatureConnector}
-         * and slot id.
+         * @return a {@link FeatureConnector} associated for the given {@link FeatureUpdates}
+         * and slot index.
          */
-        FeatureConnector<T> create(Context context, int slotId,
-                FeatureConnector.Listener<T> listener, Executor executor, String tag);
+        FeatureConnector<U> create(Context context, int slotIndex,
+                FeatureConnector.Listener<U> listener, Executor executor, String logPrefix);
     }
 
     /**
@@ -100,7 +102,8 @@
                 ImsRegistrationCallbackHelper.ImsRegistrationUpdate cb, Executor executor);
     }
 
-    private FeatureConnectorFactory<RcsFeatureManager> mFeatureFactory = FeatureConnector::new;
+    private FeatureConnectorFactory<RcsFeatureManager> mFeatureFactory =
+            RcsFeatureManager::getConnector;
     private RegistrationHelperFactory mRegistrationHelperFactory =
             ImsRegistrationCallbackHelper::new;
 
@@ -115,11 +118,6 @@
     private FeatureConnector.Listener<RcsFeatureManager> mFeatureConnectorListener =
             new FeatureConnector.Listener<RcsFeatureManager>() {
                 @Override
-                public RcsFeatureManager getFeatureManager() {
-                    return new RcsFeatureManager(mContext, mSlotId);
-                }
-
-                @Override
                 public void connectionReady(RcsFeatureManager manager)
                         throws com.android.ims.ImsException {
                     if (manager == null) {
@@ -140,7 +138,10 @@
                 }
 
                 @Override
-                public void connectionUnavailable() {
+                public void connectionUnavailable(int reason) {
+                    if (reason == FeatureConnector.UNAVAILABLE_REASON_SERVER_UNAVAILABLE) {
+                        loge("unexpected - connectionUnavailable due to server unavailable");
+                    }
                     // Call before disabling connection to manager.
                     removeConnectionToService();
                     updateConnectionStatus(null /*manager*/);
@@ -279,7 +280,7 @@
     }
 
     @VisibleForTesting
-    public void setFeatureConnectorFactory(FeatureConnectorFactory factory) {
+    public void setFeatureConnectorFactory(FeatureConnectorFactory<RcsFeatureManager> factory) {
         mFeatureFactory = factory;
     }
 
@@ -433,6 +434,10 @@
         Log.w(LOG_TAG, getLogPrefix().append(log).toString());
     }
 
+    private void loge(String log) {
+        Log.e(LOG_TAG, getLogPrefix().append(log).toString());
+    }
+
     private StringBuilder getLogPrefix() {
         StringBuilder sb = new StringBuilder("[");
         sb.append(mSlotId);
diff --git a/tests/src/com/android/services/telephony/TestTelephonyConnection.java b/tests/src/com/android/services/telephony/TestTelephonyConnection.java
index 09cec17..2635e60 100644
--- a/tests/src/com/android/services/telephony/TestTelephonyConnection.java
+++ b/tests/src/com/android/services/telephony/TestTelephonyConnection.java
@@ -169,6 +169,12 @@
         // Requires ImsManager dependencies, do not implement during testing.
     }
 
+    @Override
+    boolean isWfcEnabled(Phone phone) {
+        // Requires ImsManager dependencies, mock for test.
+        return true;
+    }
+
     public int getNotifyPhoneAccountChangedCount() {
         return mNotifyPhoneAccountChangedCount;
     }
diff --git a/tests/src/com/android/services/telephony/rcs/RcsFeatureControllerTest.java b/tests/src/com/android/services/telephony/rcs/RcsFeatureControllerTest.java
index fbb270d..7e87dc7 100644
--- a/tests/src/com/android/services/telephony/rcs/RcsFeatureControllerTest.java
+++ b/tests/src/com/android/services/telephony/rcs/RcsFeatureControllerTest.java
@@ -100,7 +100,8 @@
         verify(mMockFeature).onRcsConnected(mFeatureManager);
 
         // Disconnect
-        mConnectorListener.getValue().connectionUnavailable();
+        mConnectorListener.getValue().connectionUnavailable(
+                FeatureConnector.UNAVAILABLE_REASON_DISCONNECTED);
 
         verify(mFeatureManager).unregisterImsRegistrationCallback(any());
         verify(mMockFeature, times(2)).onRcsDisconnected();
@@ -193,7 +194,8 @@
     public void testFeatureManagerDisconnectedAddFeature() {
         RcsFeatureController controller = createFeatureController();
         // Disconnect the RcsFeatureManager
-        mConnectorListener.getValue().connectionUnavailable();
+        mConnectorListener.getValue().connectionUnavailable(
+                FeatureConnector.UNAVAILABLE_REASON_DISCONNECTED);
         controller.addFeature(mMockFeature, RcsFeatureController.Feature.class);
 
         verify(mMockFeature).onRcsDisconnected();
@@ -205,7 +207,8 @@
         IImsRegistrationCallback regCb = mock(IImsRegistrationCallback.class);
         IImsCapabilityCallback capCb = mock(IImsCapabilityCallback.class);
         // Disconnect the RcsFeatureManager
-        mConnectorListener.getValue().connectionUnavailable();
+        mConnectorListener.getValue().connectionUnavailable(
+                FeatureConnector.UNAVAILABLE_REASON_DISCONNECTED);
 
         try {
             controller.registerImsRegistrationCallback(0 /*subId*/, null /*callback*/);