Merge "Fix issue that the disconnect tone doesn't sound from BTHS" am: 0a17b57d1c am: fef8b02b63
am: 99ba29fd35

Change-Id: I6376c69016d82af6dfd558b73192ea0270bb9b8f
diff --git a/src/com/android/server/telecom/BluetoothPhoneServiceImpl.java b/src/com/android/server/telecom/BluetoothPhoneServiceImpl.java
index 93d4f71..d0fcfa1 100644
--- a/src/com/android/server/telecom/BluetoothPhoneServiceImpl.java
+++ b/src/com/android/server/telecom/BluetoothPhoneServiceImpl.java
@@ -66,6 +66,7 @@
     private static final int CALL_STATE_INCOMING = 4;
     private static final int CALL_STATE_WAITING = 5;
     private static final int CALL_STATE_IDLE = 6;
+    private static final int CALL_STATE_DISCONNECTED = 7;
 
     // match up with bthf_call_state_t of bt_hf.h
     // Terminate all held or set UDUB("busy") to a waiting call
@@ -84,6 +85,7 @@
     private String mRingingAddress = null;
     private int mRingingAddressType = 0;
     private Call mOldHeldCall = null;
+    private boolean mIsDisconnectedTonePlaying = false;
 
     /**
      * Binder implementation of IBluetoothHeadsetPhone. Implements the command interface that the
@@ -387,6 +389,12 @@
             }
             updateHeadsetWithCallState(false /* force */);
         }
+
+        @Override
+        public void onDisconnectedTonePlaying(boolean isTonePlaying) {
+            mIsDisconnectedTonePlaying = isTonePlaying;
+            updateHeadsetWithCallState(false /* force */);
+        }
     };
 
     /**
@@ -816,6 +824,7 @@
         CallsManager callsManager = mCallsManager;
         Call ringingCall = mCallsManager.getRingingCall();
         Call dialingCall = mCallsManager.getOutgoingCall();
+        boolean hasOnlyDisconnectedCalls = mCallsManager.hasOnlyDisconnectedCalls();
 
         //
         // !! WARNING !!
@@ -831,6 +840,9 @@
             bluetoothCallState = CALL_STATE_INCOMING;
         } else if (dialingCall != null) {
             bluetoothCallState = CALL_STATE_ALERTING;
+        } else if (hasOnlyDisconnectedCalls || mIsDisconnectedTonePlaying) {
+            // Keep the DISCONNECTED state until the disconnect tone's playback is done
+            bluetoothCallState = CALL_STATE_DISCONNECTED;
         }
         return bluetoothCallState;
     }
diff --git a/src/com/android/server/telecom/CallAudioManager.java b/src/com/android/server/telecom/CallAudioManager.java
index bf1ef8f..ab06209 100644
--- a/src/com/android/server/telecom/CallAudioManager.java
+++ b/src/com/android/server/telecom/CallAudioManager.java
@@ -56,6 +56,7 @@
 
     private Call mForegroundCall;
     private boolean mIsTonePlaying = false;
+    private boolean mIsDisconnectedTonePlaying = false;
     private InCallTonePlayer mHoldTonePlayer;
 
     public CallAudioManager(CallAudioRouteStateMachine callAudioRouteStateMachine,
@@ -519,6 +520,11 @@
                 isTonePlaying ? CallAudioModeStateMachine.TONE_STARTED_PLAYING
                         : CallAudioModeStateMachine.TONE_STOPPED_PLAYING,
                 makeArgsForModeStateMachine());
+
+        if (!isTonePlaying && mIsDisconnectedTonePlaying) {
+            mCallsManager.onDisconnectedTonePlaying(false);
+            mIsDisconnectedTonePlaying = false;
+        }
     }
 
     private void onCallLeavingState(Call call, int state) {
@@ -699,6 +705,8 @@
 
             if (toneToPlay != InCallTonePlayer.TONE_INVALID) {
                 mPlayerFactory.createPlayer(toneToPlay).startTone();
+                mCallsManager.onDisconnectedTonePlaying(true);
+                mIsDisconnectedTonePlaying = true;
             }
         }
     }
@@ -792,4 +800,4 @@
     public SparseArray<LinkedHashSet<Call>> getCallStateToCalls() {
         return mCallStateToCalls;
     }
-}
\ No newline at end of file
+}
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index ad8ac43..114fec9 100644
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -119,6 +119,7 @@
         void onSessionModifyRequestReceived(Call call, VideoProfile videoProfile);
         void onHoldToneRequested(Call call);
         void onExternalCallChanged(Call call, boolean isExternalCall);
+        void onDisconnectedTonePlaying(boolean isTonePlaying);
     }
 
     private static final String TAG = "CallsManager";
@@ -773,7 +774,11 @@
         return false;
     }
 
-    boolean hasOnlyDisconnectedCalls() {
+    @VisibleForTesting
+    public boolean hasOnlyDisconnectedCalls() {
+        if (mCalls.size() == 0) {
+            return false;
+        }
         for (Call call : mCalls) {
             if (!call.isDisconnected()) {
                 return false;
@@ -1788,6 +1793,20 @@
         }
     }
 
+    /**
+     * Called when disconnect tone is started or stopped, including any InCallTone
+     * after disconnected call.
+     *
+     * @param isTonePlaying true if the disconnected tone is started, otherwise the disconnected
+     * tone is stopped.
+     */
+    void onDisconnectedTonePlaying(boolean isTonePlaying) {
+        Log.v(this, "onDisconnectedTonePlaying, %s", isTonePlaying ? "started" : "stopped");
+        for (CallsManagerListener listener : mListeners) {
+            listener.onDisconnectedTonePlaying(isTonePlaying);
+        }
+    }
+
     void markCallAsRinging(Call call) {
         setCallState(call, CallState.RINGING, "ringing set explicitly");
     }
diff --git a/src/com/android/server/telecom/CallsManagerListenerBase.java b/src/com/android/server/telecom/CallsManagerListenerBase.java
index a4c76c1..c0a71eb 100644
--- a/src/com/android/server/telecom/CallsManagerListenerBase.java
+++ b/src/com/android/server/telecom/CallsManagerListenerBase.java
@@ -88,4 +88,8 @@
     @Override
     public void onExternalCallChanged(Call call, boolean isExternalCall) {
     }
+
+    @Override
+    public void onDisconnectedTonePlaying(boolean isTonePlaying) {
+    }
 }
diff --git a/tests/src/com/android/server/telecom/tests/BluetoothPhoneServiceTest.java b/tests/src/com/android/server/telecom/tests/BluetoothPhoneServiceTest.java
index 300415a..885fbc4 100644
--- a/tests/src/com/android/server/telecom/tests/BluetoothPhoneServiceTest.java
+++ b/tests/src/com/android/server/telecom/tests/BluetoothPhoneServiceTest.java
@@ -78,6 +78,7 @@
     private static final int CALL_STATE_INCOMING = 4;
     private static final int CALL_STATE_WAITING = 5;
     private static final int CALL_STATE_IDLE = 6;
+    private static final int CALL_STATE_DISCONNECTED = 7;
     // Terminate all held or set UDUB("busy") to a waiting call
     private static final int CHLD_TYPE_RELEASEHELD = 0;
     // Terminate all active calls and accepts a waiting/held call
@@ -110,6 +111,7 @@
         doReturn(null).when(mMockCallsManager).getHeldCall();
         doReturn(null).when(mMockCallsManager).getOutgoingCall();
         doReturn(0).when(mMockCallsManager).getNumHeldCalls();
+        doReturn(false).when(mMockCallsManager).hasOnlyDisconnectedCalls();
         mBluetoothPhoneService = new BluetoothPhoneServiceImpl(mContext, mLock, mMockCallsManager,
                 mock(BluetoothAdapterProxy.class), mMockPhoneAccountRegistrar);
 
@@ -817,6 +819,25 @@
     }
 
     @MediumTest
+    public void testOnCallStateChangedDisconnected() throws Exception {
+        Call disconnectedCall = createDisconnectedCall();
+        doReturn(true).when(mMockCallsManager).hasOnlyDisconnectedCalls();
+        mBluetoothPhoneService.mCallsManagerListener.onCallStateChanged(disconnectedCall,
+                CallState.DISCONNECTING, CallState.DISCONNECTED);
+        verify(mMockBluetoothHeadset).phoneStateChanged(eq(0), eq(0), eq(CALL_STATE_DISCONNECTED),
+                eq(""), eq(128));
+
+        doReturn(false).when(mMockCallsManager).hasOnlyDisconnectedCalls();
+        mBluetoothPhoneService.mCallsManagerListener.onDisconnectedTonePlaying(true);
+        verify(mMockBluetoothHeadset).phoneStateChanged(eq(0), eq(0), eq(CALL_STATE_DISCONNECTED),
+                eq(""), eq(128));
+
+        mBluetoothPhoneService.mCallsManagerListener.onDisconnectedTonePlaying(false);
+        verify(mMockBluetoothHeadset).phoneStateChanged(eq(0), eq(0), eq(CALL_STATE_IDLE),
+                eq(""), eq(128));
+    }
+
+    @MediumTest
     public void testOnCallStateChanged() throws Exception {
         Call ringingCall = createRingingCall();
         when(ringingCall.getHandle()).thenReturn(Uri.parse("tel:555-0000"));
@@ -929,6 +950,12 @@
         return call;
     }
 
+    private Call createDisconnectedCall() {
+        Call call = mock(Call.class);
+        when(mMockCallsManager.getFirstCallWithState(CallState.DISCONNECTED)).thenReturn(call);
+        return call;
+    }
+
     private Call createForegroundCall() {
         Call call = mock(Call.class);
         when(mMockCallsManager.getForegroundCall()).thenReturn(call);