Merge "Don't stop outgoing calls if we are the foreground call." into lmp-dev
diff --git a/src/com/android/server/telecom/BluetoothPhoneService.java b/src/com/android/server/telecom/BluetoothPhoneService.java
index efac3bf..9e02539 100644
--- a/src/com/android/server/telecom/BluetoothPhoneService.java
+++ b/src/com/android/server/telecom/BluetoothPhoneService.java
@@ -30,6 +30,7 @@
import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
+import android.telecom.CallState;
import android.telecom.PhoneAccount;
import android.telephony.PhoneNumberUtils;
import android.telephony.TelephonyManager;
@@ -37,7 +38,10 @@
import com.android.server.telecom.CallsManager.CallsManagerListener;
+import java.util.Collection;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
/**
* Bluetooth headset manager for Telecom. This class shares the call state with the bluetooth device
@@ -272,8 +276,11 @@
break;
case MSG_LIST_CURRENT_CALLS:
- // TODO - Add current calls.
- request.setResult(true);
+ try {
+ sendListOfCalls();
+ } finally {
+ request.setResult(true);
+ }
break;
case MSG_QUERY_PHONE_STATE:
@@ -301,6 +308,7 @@
@Override
public void onCallRemoved(Call call) {
+ mClccIndexMap.remove(call);
updateHeadsetWithCallState();
}
@@ -354,6 +362,9 @@
private BluetoothAdapter mBluetoothAdapter;
private BluetoothHeadset mBluetoothHeadset;
+ // A map from Calls to indexes used to identify calls for CLCC (C* List Current Calls).
+ private Map<Call, Integer> mClccIndexMap = new HashMap<>();
+
public BluetoothPhoneService() {
Log.v(TAG, "Constructor");
}
@@ -472,6 +483,65 @@
return null;
}
+ private void sendListOfCalls() {
+ Collection<Call> mCalls = getCallsManager().getCalls();
+ for (Call call : mCalls) {
+ // We don't send the parent conference call to the bluetooth device.
+ if (!call.isConference()) {
+ sendClccForCall(call);
+ }
+ }
+ sendClccEndMarker();
+ }
+
+ /**
+ * Sends a single clcc (C* List Current Calls) event for the specified call.
+ */
+ private void sendClccForCall(Call call) {
+ boolean isForeground = getCallsManager().getForegroundCall() == call;
+ int state = convertCallState(call.getState(), isForeground);
+
+ if (state == CALL_STATE_IDLE) {
+ return;
+ }
+
+ int index = getIndexForCall(call);
+ int direction = call.isIncoming() ? 1 : 0;
+ boolean isPartOfConference = call.getParentCall() != null;
+ Uri addressUri = call.getHandle();
+ String address = addressUri == null ? null : addressUri.getSchemeSpecificPart();
+ int addressType = address == null ? -1 : PhoneNumberUtils.toaFromString(address);
+
+ Log.d(this, "sending clcc for call %d, %d, %d, %b, %s, %d",
+ index, direction, state, isPartOfConference, address, addressType);
+ mBluetoothHeadset.clccResponse(
+ index, direction, state, 0, isPartOfConference, address, addressType);
+ }
+
+ private void sendClccEndMarker() {
+ // End marker is recognized with an index value of 0. All other parameters are ignored.
+ mBluetoothHeadset.clccResponse(0 /* index */, 0, 0, 0, false, null, 0);
+ }
+
+ /**
+ * Returns the caches index for the specified call. If no such index exists, then an index is
+ * given (smallest number starting from 1 that isn't already taken).
+ */
+ private int getIndexForCall(Call call) {
+ if (mClccIndexMap.containsKey(call)) {
+ return mClccIndexMap.get(call);
+ }
+
+ int i = 1; // Indexes for bluetooth clcc are 1-based.
+ while (mClccIndexMap.containsValue(i)) {
+ i++;
+ }
+
+ // NOTE: Indexes are removed in {@link #onCallRemoved}.
+ mClccIndexMap.put(call, i);
+ return i;
+ }
+
private void updateHeadsetWithCallState() {
CallsManager callsManager = getCallsManager();
Call activeCall = callsManager.getActiveCall();
@@ -518,6 +588,7 @@
private int getBluetoothCallStateForUpdate() {
CallsManager callsManager = getCallsManager();
Call ringingCall = callsManager.getRingingCall();
+ Call dialingCall = callsManager.getDialingCall();
//
// !! WARNING !!
@@ -531,12 +602,51 @@
int bluetoothCallState = CALL_STATE_IDLE;
if (ringingCall != null) {
bluetoothCallState = CALL_STATE_INCOMING;
- } else if (callsManager.getDialingOrConnectingCall() != null) {
+ } else if (dialingCall != null) {
bluetoothCallState = CALL_STATE_ALERTING;
}
return bluetoothCallState;
}
+ private int convertCallState(int callState, boolean isForegroundCall) {
+ switch (callState) {
+ case CallState.NEW:
+ case CallState.ABORTED:
+ case CallState.DISCONNECTED:
+ case CallState.CONNECTING:
+ case CallState.PRE_DIAL_WAIT:
+ if (callState == CallState.CONNECTING || callState == CallState.PRE_DIAL_WAIT) {
+ Log.w(this, "convertCallState: unexpected state %s",
+ CallState.toString(callState));
+ }
+ return CALL_STATE_IDLE;
+
+ case CallState.ACTIVE:
+ return CALL_STATE_ACTIVE;
+
+ case CallState.DIALING:
+ // Yes, this is correctly returning ALERTING.
+ // "Dialing" for BT means that we have sent information to the service provider
+ // to place the call but there is no confirmation that the call is going through.
+ // When there finally is confirmation, the ringback is played which is referred to
+ // as an "alert" tone, thus, ALERTING.
+ // TODO: We should consider using the ALERTING terms in Telecom because that
+ // seems to be more industry-standard.
+ return CALL_STATE_ALERTING;
+
+ case CallState.ON_HOLD:
+ return CALL_STATE_HELD;
+
+ case CallState.RINGING:
+ if (isForegroundCall) {
+ return CALL_STATE_INCOMING;
+ } else {
+ return CALL_STATE_WAITING;
+ }
+ }
+ return CALL_STATE_IDLE;
+ }
+
private CallsManager getCallsManager() {
return CallsManager.getInstance();
}
diff --git a/src/com/android/server/telecom/CallAudioManager.java b/src/com/android/server/telecom/CallAudioManager.java
index e3b2e42..1e305e2 100644
--- a/src/com/android/server/telecom/CallAudioManager.java
+++ b/src/com/android/server/telecom/CallAudioManager.java
@@ -445,6 +445,7 @@
case CallState.ACTIVE:
case CallState.ON_HOLD:
case CallState.DIALING:
+ case CallState.CONNECTING:
case CallState.RINGING:
route = AudioState.ROUTE_BLUETOOTH;
break;
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index e044087..1840c7e 100644
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -34,6 +34,7 @@
import com.google.common.collect.ImmutableList;
import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
@@ -114,6 +115,7 @@
private final Context mContext;
private final PhoneAccountRegistrar mPhoneAccountRegistrar;
private final MissedCallNotifier mMissedCallNotifier;
+ private final Set<Call> mLocallyDisconnectingCalls = new HashSet<>();
/**
* The call the user is currently interacting with. This is the call that should have audio
@@ -571,6 +573,7 @@
if (!mCalls.contains(call)) {
Log.w(this, "Unknown call (%s) asked to disconnect", call);
} else {
+ mLocallyDisconnectingCalls.add(call);
call.disconnect();
}
}
@@ -709,7 +712,6 @@
void markCallAsDisconnected(Call call, DisconnectCause disconnectCause) {
call.setDisconnectCause(disconnectCause);
setCallState(call, CallState.DISCONNECTED);
- removeCall(call);
}
/**
@@ -717,6 +719,12 @@
*/
void markCallAsRemoved(Call call) {
removeCall(call);
+ if (mLocallyDisconnectingCalls.contains(call)) {
+ mLocallyDisconnectingCalls.remove(call);
+ if (mForegroundCall != null && mForegroundCall.getState() == CallState.ON_HOLD) {
+ mForegroundCall.unhold();
+ }
+ }
}
/**
@@ -810,8 +818,8 @@
return getFirstCallWithState(CallState.ACTIVE);
}
- Call getDialingOrConnectingCall() {
- return getFirstCallWithState(CallState.DIALING, CallState.CONNECTING);
+ Call getDialingCall() {
+ return getFirstCallWithState(CallState.DIALING);
}
Call getHeldCall() {
@@ -1009,9 +1017,7 @@
Log.v(this, "Updating foreground call, %s -> %s.", mForegroundCall, newForegroundCall);
Call oldForegroundCall = mForegroundCall;
mForegroundCall = newForegroundCall;
- if (mForegroundCall != null && mForegroundCall.getState() == CallState.ON_HOLD) {
- mForegroundCall.unhold();
- }
+
for (CallsManagerListener listener : mListeners) {
listener.onForegroundCallChanged(oldForegroundCall, mForegroundCall);
}