Use video pause signaling when data limit reached.

When the data limit is reached and video is disabled, use the "pause"
video signaling instead of disconnecting the call.
Previously, ImsManager#updateImsServiceConfig would be called when data
was disabled due to hitting the data limit.  This would cause VT to be
unprovisioned; as a result it was not possible pause or unpause the video.
To solve this, when the data limit is reached and we're about to pause,
we are now deferring the updateImsServiceConfig until the call terminates.

Test: manual
Bug: 30760683
Change-Id: Ia1d4247cb5a11da7694e9d197a506442c7f675f4
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
index fe3a86a..2a48d4e 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
@@ -65,6 +65,7 @@
 import com.android.ims.ImsUtInterface;
 import com.android.ims.internal.IImsVideoCallProvider;
 import com.android.ims.internal.ImsVideoCallProviderWrapper;
+import com.android.ims.internal.VideoPauseTracker;
 import com.android.internal.telephony.Call;
 import com.android.internal.telephony.CallStateException;
 import com.android.internal.telephony.CallTracker;
@@ -260,6 +261,7 @@
     private Call.SrvccState mSrvccState = Call.SrvccState.NONE;
 
     private boolean mIsInEmergencyCall = false;
+    private boolean mIsDataEnabled = false;
 
     private int pendingCallClirMode;
     private int mPendingCallVideoState;
@@ -315,6 +317,13 @@
     private boolean mNotifyHandoverVideoFromWifiToLTE = false;
 
     /**
+     * Carrier configuration option which determines whether the carrier supports the
+     * {@link VideoProfile#STATE_PAUSED} signalling.
+     * See {@link CarrierConfigManager#KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL} for more information.
+     */
+    private boolean mSupportPauseVideo = false;
+
+    /**
      * Carrier configuration option which defines a mapping from pairs of
      * {@link ImsReasonInfo#getCode()} and {@link ImsReasonInfo#getExtraMessage()} values to a new
      * {@code ImsReasonInfo#CODE_*} value.
@@ -323,6 +332,17 @@
      */
     private Map<Pair<Integer, String>, Integer> mImsReasonCodeMap = new ArrayMap<>();
 
+    /**
+     * TODO: Remove this code; it is a workaround.
+     * When {@code true}, forces {@link ImsManager#updateImsServiceConfig(Context, int, boolean)} to
+     * be called when an ongoing video call is disconnected.  In some cases, where video pause is
+     * supported by the carrier, when {@link #onDataEnabledChanged(boolean, int)} reports that data
+     * has been disabled we will pause the video rather than disconnecting the call.  When this
+     * happens we need to prevent the IMS service config from being updated, as this will cause VT
+     * to be disabled mid-call, resulting in an inability to un-pause the video.
+     */
+    private boolean mShouldUpdateImsConfigOnDisconnect = false;
+
     //***** Events
 
 
@@ -574,6 +594,8 @@
                 CarrierConfigManager.KEY_NOTIFY_VT_HANDOVER_TO_WIFI_FAILURE_BOOL);
         mIgnoreDataEnabledChangedForVideoCalls = carrierConfig.getBoolean(
                 CarrierConfigManager.KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS);
+        mSupportPauseVideo = carrierConfig.getBoolean(
+                CarrierConfigManager.KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL);
 
         String[] mappings = carrierConfig
                 .getStringArray(CarrierConfigManager.KEY_IMS_REASONINFO_MAPPING_STRING_ARRAY);
@@ -1649,6 +1671,13 @@
                     mCallExpectedToResume = null;
                 }
             }
+
+            if (mShouldUpdateImsConfigOnDisconnect) {
+                // Ensure we update the IMS config when the call is disconnected; we delayed this
+                // because a video call was paused.
+                ImsManager.updateImsServiceConfig(mPhone.getContext(), mPhone.getPhoneId(), true);
+                mShouldUpdateImsConfigOnDisconnect = false;
+            }
         }
 
         @Override
@@ -2733,6 +2762,7 @@
         log("onDataEnabledChanged: enabled=" + enabled + ", reason=" + reason);
 
         ImsManager.getInstance(mPhone.getContext(), mPhone.getPhoneId()).setDataEnabled(enabled);
+        mIsDataEnabled = enabled;
 
         if (mIgnoreDataEnabledChangedForVideoCalls) {
             log("Ignore data " + ((enabled) ? "enabled" : "disabled") + " due to carrier policy.");
@@ -2770,9 +2800,12 @@
                                     TelephonyManager.EVENT_DOWNGRADE_DATA_LIMIT_REACHED, null);
                         }
                         modifyVideoCall(imsCall, VideoProfile.STATE_AUDIO_ONLY);
+                    } else if (mSupportPauseVideo) {
+                        // The carrier supports video pause signalling, so pause the video.
+                        mShouldUpdateImsConfigOnDisconnect = true;
+                        conn.pauseVideo(VideoPauseTracker.SOURCE_DATA_ENABLED);
                     } else {
-                        // If the carrier does not support downgrading to voice, the only choice we
-                        // have is to terminate the call.
+                        // At this point the only choice we have is to terminate the call.
                         try {
                             imsCall.terminate(ImsReasonInfo.CODE_USER_TERMINATED, reasonCode);
                         } catch (ImsException ie) {
@@ -2781,11 +2814,27 @@
                     }
                 }
             }
+        } else if (mSupportPauseVideo) {
+            // Data was re-enabled, so un-pause previously paused video calls.
+            for (ImsPhoneConnection conn : mConnections) {
+                // If video is paused, check to see if there are any pending pauses due to enabled
+                // state of data changing.
+                log("onDataEnabledChanged - resuming " + conn);
+                if (VideoProfile.isPaused(conn.getVideoState()) &&
+                        conn.wasVideoPausedFromSource(VideoPauseTracker.SOURCE_DATA_ENABLED)) {
+                    // The data enabled state was a cause of a pending pause, so potentially
+                    // resume the video now.
+                    conn.resumeVideo(VideoPauseTracker.SOURCE_DATA_ENABLED);
+                }
+            }
+            mShouldUpdateImsConfigOnDisconnect = false;
         }
 
-        // This will call into updateVideoCallFeatureValue and eventually all clients will be
-        // asynchronously notified that the availability of VT over LTE has changed.
-        ImsManager.updateImsServiceConfig(mPhone.getContext(), mPhone.getPhoneId(), true);
+        if (!mShouldUpdateImsConfigOnDisconnect) {
+            // This will call into updateVideoCallFeatureValue and eventually all clients will be
+            // asynchronously notified that the availability of VT over LTE has changed.
+            ImsManager.updateImsServiceConfig(mPhone.getContext(), mPhone.getPhoneId(), true);
+        }
     }
 
     /**
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
index 9ed6fea..99526c4 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
@@ -100,6 +100,8 @@
      */
     private boolean mShouldIgnoreVideoStateChanges = false;
 
+    private ImsVideoCallProviderWrapper mImsVideoCallProviderWrapper;
+
     //***** Event Constants
     private static final int EVENT_DTMF_DONE = 1;
     private static final int EVENT_PAUSE_DONE = 2;
@@ -996,6 +998,15 @@
         return sb.toString();
     }
 
+    @Override
+    public void setVideoProvider(android.telecom.Connection.VideoProvider videoProvider) {
+        super.setVideoProvider(videoProvider);
+
+        if (videoProvider instanceof ImsVideoCallProviderWrapper) {
+            mImsVideoCallProviderWrapper = (ImsVideoCallProviderWrapper) videoProvider;
+        }
+    }
+
     /**
      * Indicates whether current phone connection is emergency or not
      * @return boolean: true if emergency, false otherwise
@@ -1047,4 +1058,46 @@
             setVideoState(currentVideoState);
         }
     }
+
+    /**
+     * Issues a request to pause the video using {@link VideoProfile#STATE_PAUSED} from a source
+     * other than the InCall UI.
+     *
+     * @param source The source of the pause request.
+     */
+    public void pauseVideo(int source) {
+        if (mImsVideoCallProviderWrapper == null) {
+            return;
+        }
+
+        mImsVideoCallProviderWrapper.pauseVideo(getVideoState(), source);
+    }
+
+    /**
+     * Issues a request to resume the video using {@link VideoProfile#STATE_PAUSED} from a source
+     * other than the InCall UI.
+     *
+     * @param source The source of the resume request.
+     */
+    public void resumeVideo(int source) {
+        if (mImsVideoCallProviderWrapper == null) {
+            return;
+        }
+
+        mImsVideoCallProviderWrapper.resumeVideo(getVideoState(), source);
+    }
+
+    /**
+     * Determines if a specified source has issued a pause request.
+     *
+     * @param source The source.
+     * @return {@code true} if the source issued a pause request, {@code false} otherwise.
+     */
+    public boolean wasVideoPausedFromSource(int source) {
+        if (mImsVideoCallProviderWrapper == null) {
+            return false;
+        }
+
+        return mImsVideoCallProviderWrapper.wasVideoPausedFromSource(source);
+    }
 }