RTT initialization bugfixes

Properly dispose of old RTT data pipes
Add logging
Read the system setting to see if we should start the call with RTT
Deal with connection managers that possibly support RTT

Bug: 71873552
Test: manual
Change-Id: I12f67ca977bd97302c7e5a21a7374f085e5865ce
diff --git a/src/com/android/server/telecom/Call.java b/src/com/android/server/telecom/Call.java
index 420b71d..bb943ee 100644
--- a/src/com/android/server/telecom/Call.java
+++ b/src/com/android/server/telecom/Call.java
@@ -60,6 +60,7 @@
 import java.lang.String;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.Date;
 import java.util.LinkedList;
@@ -484,6 +485,17 @@
      */
     private ParcelFileDescriptor[] mInCallToConnectionServiceStreams;
     private ParcelFileDescriptor[] mConnectionServiceToInCallStreams;
+
+    /**
+     * Abandoned RTT pipes, to be cleaned up when the call is removed
+     */
+    private Collection<ParcelFileDescriptor> mDiscardedRttFds = new LinkedList<>();
+
+    /**
+     * True if we're supposed to start this call with RTT, either due to the master switch or due
+     * to an extra.
+     */
+    private boolean mDidRequestToStartWithRtt = false;
     /**
      * Integer constant from {@link android.telecom.Call.RttCall}. Describes the current RTT mode.
      */
@@ -659,6 +671,15 @@
             mCallerInfo.cachedPhotoIcon = null;
             mCallerInfo.cachedPhoto = null;
         }
+        for (ParcelFileDescriptor fd : mDiscardedRttFds) {
+            if (fd != null) {
+                try {
+                    fd.close();
+                } catch (IOException e) {
+                    // ignore
+                }
+            }
+        }
         Log.addEvent(this, LogUtils.Events.DESTROYED);
     }
 
@@ -1068,7 +1089,7 @@
                 l.onConnectionManagerPhoneAccountChanged(this);
             }
         }
-
+        checkIfRttCapable();
     }
 
     @VisibleForTesting
@@ -1086,6 +1107,7 @@
             configureCallAttributes();
         }
         checkIfVideoCapable();
+        checkIfRttCapable();
     }
 
     public CharSequence getTargetPhoneAccountLabel() {
@@ -1264,6 +1286,37 @@
         }
     }
 
+    private void checkIfRttCapable() {
+        PhoneAccountRegistrar phoneAccountRegistrar = mCallsManager.getPhoneAccountRegistrar();
+        if (mTargetPhoneAccountHandle == null) {
+            return;
+        }
+
+        // Check both the target phone account and the connection manager phone account -- if
+        // either support RTT, just set the streams and have them set/unset the RTT property as
+        // needed.
+        PhoneAccount phoneAccount =
+                phoneAccountRegistrar.getPhoneAccountUnchecked(mTargetPhoneAccountHandle);
+        PhoneAccount connectionManagerPhoneAccount = phoneAccountRegistrar.getPhoneAccountUnchecked(
+                        mConnectionManagerPhoneAccountHandle);
+        boolean isRttSupported = phoneAccount != null && phoneAccount.hasCapabilities(
+                PhoneAccount.CAPABILITY_RTT);
+        boolean isConnectionManagerRttSupported = connectionManagerPhoneAccount != null
+                && connectionManagerPhoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_RTT);
+
+        if ((isConnectionManagerRttSupported || isRttSupported)
+                && mDidRequestToStartWithRtt && !areRttStreamsInitialized()) {
+            // If the phone account got set to an RTT capable one and we haven't set the streams
+            // yet, do so now.
+            setRttStreams(true);
+            Log.i(this, "Setting RTT streams after target phone account selected");
+        } else if (!isRttSupported && !isConnectionManagerRttSupported) {
+            // If the phone account got set to RTT-incapable, unset the streams.
+            Log.i(this, "Unsetting RTT streams after target phone account selected");
+            setRttStreams(false);
+        }
+    }
+
     boolean shouldAttachToExistingConnection() {
         return mShouldAttachToExistingConnection;
     }
@@ -2414,6 +2467,10 @@
         return mSpeakerphoneOn;
     }
 
+    public void setRequestedToStartWithRtt() {
+        mDidRequestToStartWithRtt = true;
+    }
+
     public void stopRtt() {
         if (mConnectionService != null) {
             mConnectionService.stopRtt(this);
@@ -2429,9 +2486,14 @@
         mConnectionService.startRtt(this, getInCallToCsRttPipeForCs(), getCsToInCallRttPipeForCs());
     }
 
-    public void setRttStreams(boolean shouldBeRtt) {
-        boolean areStreamsInitialized = mInCallToConnectionServiceStreams != null
+    private boolean areRttStreamsInitialized() {
+        return mInCallToConnectionServiceStreams != null
                 && mConnectionServiceToInCallStreams != null;
+    }
+
+    public void setRttStreams(boolean shouldBeRtt) {
+        boolean areStreamsInitialized = areRttStreamsInitialized();
+        Log.i(this, "Setting RTT streams to %b, currently %b", shouldBeRtt, areStreamsInitialized);
         if (shouldBeRtt && !areStreamsInitialized) {
             try {
                 mWasEverRtt = true;
@@ -2448,6 +2510,7 @@
     }
 
     public void onRttConnectionFailure(int reason) {
+        Log.i(this, "Got RTT initiation failure with reason %d", reason);
         setRttStreams(false);
         for (Listener l : mListeners) {
             l.onRttInitiationFailure(this, reason);
@@ -2487,7 +2550,15 @@
     }
 
     public void closeRttPipes() {
-        // TODO: may defer this until call is removed?
+        // Defer closing until the call is destroyed
+        if (mInCallToConnectionServiceStreams != null) {
+            mDiscardedRttFds.add(mInCallToConnectionServiceStreams[0]);
+            mDiscardedRttFds.add(mInCallToConnectionServiceStreams[1]);
+        }
+        if (mConnectionServiceToInCallStreams != null) {
+            mDiscardedRttFds.add(mConnectionServiceToInCallStreams[0]);
+            mDiscardedRttFds.add(mConnectionServiceToInCallStreams[1]);
+        }
     }
 
     public boolean isRttCall() {
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index 82e3787..e307d5f 100644
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -915,11 +915,16 @@
                 call.setIsVoipAudioMode(true);
             }
         }
-        if (extras.getBoolean(TelecomManager.EXTRA_START_CALL_WITH_RTT, false)) {
+        if (isRttSettingOn() ||
+                extras.getBoolean(TelecomManager.EXTRA_START_CALL_WITH_RTT, false)) {
+            Log.d(this, "Incoming call requesting RTT, rtt setting is %b", isRttSettingOn());
             if (phoneAccount != null &&
                     phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_RTT)) {
                 call.setRttStreams(true);
             }
+            // Even if the phone account doesn't support RTT yet, the connection manager might
+            // change that. Set this to check it later.
+            call.setRequestedToStartWithRtt();
         }
         // If the extras specifies a video state, set it on the call if the PhoneAccount supports
         // video.
@@ -1201,12 +1206,16 @@
                     CallState.CONNECTING,
                     phoneAccountHandle == null ? "no-handle" : phoneAccountHandle.toString());
 
-            if (extras != null
-                    && extras.getBoolean(TelecomManager.EXTRA_START_CALL_WITH_RTT, false)) {
+            if (isRttSettingOn() || (extras != null
+                    && extras.getBoolean(TelecomManager.EXTRA_START_CALL_WITH_RTT, false))) {
+                Log.d(this, "Outgoing call requesting RTT, rtt setting is %b", isRttSettingOn());
                 if (accountToUse != null
                         && accountToUse.hasCapabilities(PhoneAccount.CAPABILITY_RTT)) {
                     call.setRttStreams(true);
                 }
+                // Even if the phone account doesn't support RTT yet, the connection manager might
+                // change that. Set this to check it later.
+                call.setRequestedToStartWithRtt();
             }
         }
         setIntentExtrasAndStartTime(call, extras);
@@ -1753,6 +1762,12 @@
         mProximitySensorManager.turnOff(screenOnImmediately);
     }
 
+    private boolean isRttSettingOn() {
+        return Settings.System.getInt(mContext.getContentResolver(),
+                Settings.System.RTT_CALLING_MODE, TelecomManager.TTY_MODE_OFF)
+                != TelecomManager.TTY_MODE_OFF;
+    }
+
     void phoneAccountSelected(Call call, PhoneAccountHandle account, boolean setDefault) {
         if (!mCalls.contains(call)) {
             Log.i(this, "Attempted to add account to unknown call %s", call);
@@ -1766,12 +1781,17 @@
                 Log.d("phoneAccountSelected: default to voip mode for call %s", call.getId());
                 call.setIsVoipAudioMode(true);
             }
-            if (call.getIntentExtras()
+            if (isRttSettingOn() || call.getIntentExtras()
                     .getBoolean(TelecomManager.EXTRA_START_CALL_WITH_RTT, false)) {
+                Log.d(this, "Outgoing call after account selection requesting RTT," +
+                        " rtt setting is %b", isRttSettingOn());
                 if (realPhoneAccount != null
                         && realPhoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_RTT)) {
                     call.setRttStreams(true);
                 }
+                // Even if the phone account doesn't support RTT yet, the connection manager might
+                // change that. Set this to check it later.
+                call.setRequestedToStartWithRtt();
             }
 
             if (!call.isNewOutgoingCallIntentBroadcastDone()) {