Merge "Enforce the subId of a request matches the active subId for IMS"
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index acf6b39..b501892 100755
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -4031,16 +4031,12 @@
             throws RemoteException {
         TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "registerImsRegistrationCallback");
-
-        if (!ImsManager.isImsSupportedOnDevice(mApp)) {
-            throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
-                    "IMS not available on device.");
-        }
         final long token = Binder.clearCallingIdentity();
         try {
-            int slotId = getSlotIndexOrException(subId);
-            verifyImsMmTelConfiguredOrThrow(slotId);
-            ImsManager.getInstance(mApp, slotId).addRegistrationCallbackForSubscription(c, subId);
+            runUsingImsManagerOrThrow(subId, (ImsFunction<Void>) manager -> {
+                manager.addRegistrationCallbackForSubscription(c, subId);
+                return null;
+            });
         } catch (ImsException e) {
             throw new ServiceSpecificException(e.getCode());
         } finally {
@@ -4153,15 +4149,12 @@
             throws RemoteException {
         TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "registerMmTelCapabilityCallback");
-        if (!ImsManager.isImsSupportedOnDevice(mApp)) {
-            throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
-                    "IMS not available on device.");
-        }
         final long token = Binder.clearCallingIdentity();
         try {
-            int slotId = getSlotIndexOrException(subId);
-            verifyImsMmTelConfiguredOrThrow(slotId);
-            ImsManager.getInstance(mApp, slotId).addCapabilitiesCallbackForSubscription(c, subId);
+            runUsingImsManagerOrThrow(subId, (ImsFunction<Void>) manager -> {
+                manager.addCapabilitiesCallbackForSubscription(c, subId);
+                return null;
+            });
         } catch (ImsException e) {
             throw new ServiceSpecificException(e.getCode());
         } finally {
@@ -4201,12 +4194,8 @@
         enforceReadPrivilegedPermission("isCapable");
         final long token = Binder.clearCallingIdentity();
         try {
-            int slotId = getSlotIndexOrException(subId);
-            verifyImsMmTelConfiguredOrThrow(slotId);
-            return ImsManager.getInstance(mApp, slotId).queryMmTelCapability(capability, regTech);
-        } catch (com.android.ims.ImsException e) {
-            Log.w(LOG_TAG, "IMS isCapable - service unavailable: " + e.getMessage());
-            return false;
+            return runUsingImsManagerOrThrow(subId,
+                    manager -> manager.queryMmTelCapability(capability, regTech));
         } catch (ImsException e) {
             Log.i(LOG_TAG, "isCapable: " + subId + " is inactive, returning false.");
             return false;
@@ -4243,28 +4232,19 @@
     public void isMmTelCapabilitySupported(int subId, IIntegerConsumer callback, int capability,
             int transportType) {
         enforceReadPrivilegedPermission("isMmTelCapabilitySupported");
-        if (!ImsManager.isImsSupportedOnDevice(mApp)) {
-            throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
-                    "IMS not available on device.");
-        }
         final long token = Binder.clearCallingIdentity();
         try {
-            int slotId = getSlotIndex(subId);
-            if (slotId <= SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
-                Log.w(LOG_TAG, "isMmTelCapabilitySupported: called with an inactive subscription '"
-                        + subId + "'");
-                throw new ServiceSpecificException(ImsException.CODE_ERROR_INVALID_SUBSCRIPTION);
-            }
-            verifyImsMmTelConfiguredOrThrow(slotId);
-            ImsManager.getInstance(mApp, slotId).isSupported(capability,
-                    transportType, aBoolean -> {
-                        try {
-                            callback.accept((aBoolean == null) ? 0 : (aBoolean ? 1 : 0));
-                        } catch (RemoteException e) {
-                            Log.w(LOG_TAG, "isMmTelCapabilitySupported: remote caller is not "
-                                    + "running. Ignore");
-                        }
-                    });
+            runUsingImsManagerOrThrow(subId, (ImsFunction<Void>) manager -> {
+                manager.isSupported(capability, transportType, aBoolean -> {
+                    try {
+                        callback.accept((aBoolean == null) ? 0 : (aBoolean ? 1 : 0));
+                    } catch (RemoteException e) {
+                        Log.w(LOG_TAG, "isMmTelCapabilitySupported: remote caller is not "
+                                        + "running. Ignore");
+                    }
+                });
+                return null;
+            });
         } catch (ImsException e) {
             throw new ServiceSpecificException(e.getCode());
         } finally {
@@ -4595,14 +4575,10 @@
 
         final long identity = Binder.clearCallingIdentity();
         try {
-            if (!isImsAvailableOnDevice()) {
-                throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
-                        "IMS not available on device.");
-            }
-            int slotId = getSlotIndexOrException(subId);
-            verifyImsMmTelConfiguredOrThrow(slotId);
-            ImsManager.getInstance(mApp, slotId)
-                    .addProvisioningCallbackForSubscription(callback, subId);
+            runUsingImsManagerOrThrow(subId, (ImsFunction<Void>) manager -> {
+                manager.addProvisioningCallbackForSubscription(callback, subId);
+                return null;
+            });
         } catch (ImsException e) {
             throw new ServiceSpecificException(e.getCode());
         } finally {
@@ -4975,6 +4951,54 @@
         }
     }
 
+    //
+
+    /**
+     * Functional interface for encapsulating IMS related commands that should be sent to
+     * ImsManager for processing.
+     * @param <V> The return type
+     */
+    private interface ImsFunction<V> {
+        V call(ImsManager manager) throws Exception;
+    }
+
+    /**
+     * Run the specified command on the ImsManager for the subscription specified or throw an
+     * {@link ImsException} if the IMS is not available for any reason.
+     * @param subId The subscription to use
+     * @param func The function to run on the given ImsManager instance.
+     * @param <V> The return type of the function
+     * @return The result of the evaluated command
+     * @throws ImsException if the IMS service is not available for any reason.
+     */
+    private <V> V runUsingImsManagerOrThrow(int subId, ImsFunction<V> func) throws ImsException {
+        if (!ImsManager.isImsSupportedOnDevice(mApp)) {
+            throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
+                    "IMS not available on device.");
+        }
+        int slotId = getSlotIndexOrException(subId);
+        verifyImsMmTelConfiguredOrThrow(slotId);
+        ImsManager manager = ImsManager.getInstance(mApp, slotId);
+        if (manager.getSubId() != subId || !manager.isServiceReady()) {
+            Log.w(LOG_TAG, "getImsManagerForSubIdOrThrow: couldn't  resolve ImsManager, ready= "
+                    + manager.isServiceReady() + ", manager subId= " + manager.getSubId()
+                    + ", request subId= " + subId);
+            // if we have hit this point, getSlotIndexOrException has already checked that the subId
+            // is active for a phone, but ImsManager currently doesn't have the correct subId set or
+            // the ImsService has crashed/is not ready. The caller will have to wait for the service
+            // to be available before calling again.
+            throw new ImsException("The ImsService is not available for the subId specified.",
+                    ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+        try {
+            return func.call(manager);
+        } catch (Exception e) {
+            // Some methods internally report RuntimeExceptions. Repackage those exceptions as well
+            // defined ImsExceptions.
+            throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+    }
+
     private int getSlotIndexOrException(int subId) throws ImsException {
         int slotId = SubscriptionManager.getSlotIndex(subId);
         if (!SubscriptionManager.isValidSlotIndex(slotId)) {
@@ -5950,26 +5974,19 @@
     @Override
     public void getImsMmTelFeatureState(int subId, IIntegerConsumer callback) {
         enforceReadPrivilegedPermission("getImsMmTelFeatureState");
-        if (!ImsManager.isImsSupportedOnDevice(mApp)) {
-            throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
-                    "IMS not available on device.");
-        }
         final long token = Binder.clearCallingIdentity();
         try {
-            int slotId = getSlotIndex(subId);
-            if (slotId <= SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
-                Log.w(LOG_TAG, "getImsMmTelFeatureState: called with an inactive subscription '"
-                        + subId + "'");
-                throw new ServiceSpecificException(ImsException.CODE_ERROR_INVALID_SUBSCRIPTION);
-            }
-            verifyImsMmTelConfiguredOrThrow(slotId);
-            ImsManager.getInstance(mApp, slotId).getImsServiceState(anInteger -> {
-                try {
-                    callback.accept(anInteger == null ? ImsFeature.STATE_UNAVAILABLE : anInteger);
-                } catch (RemoteException e) {
-                    Log.w(LOG_TAG, "getImsMmTelFeatureState: remote caller is no longer running. "
-                            + "Ignore");
-                }
+            runUsingImsManagerOrThrow(subId, (ImsFunction<Void>) manager -> {
+                manager.getImsServiceState(anInteger -> {
+                    try {
+                        callback.accept(anInteger == null
+                                ? ImsFeature.STATE_UNAVAILABLE : anInteger);
+                    } catch (RemoteException e) {
+                        Log.w(LOG_TAG, "getImsMmTelFeatureState: remote caller is no longer "
+                                + "running. Ignore");
+                    }
+                });
+                return null;
             });
         } catch (ImsException e) {
             throw new ServiceSpecificException(e.getCode());