Merge \"Finalize multiendpoint functionality.\" into nyc-mr1-dev
am: b489d6c2a1
Change-Id: Iaf8e0b5957036a7c62a10d77bed86814ea8a67a8
diff --git a/src/com/android/server/telecom/BluetoothPhoneServiceImpl.java b/src/com/android/server/telecom/BluetoothPhoneServiceImpl.java
index 5ea199b..b649b90 100644
--- a/src/com/android/server/telecom/BluetoothPhoneServiceImpl.java
+++ b/src/com/android/server/telecom/BluetoothPhoneServiceImpl.java
@@ -344,7 +344,8 @@
// state. We can assume that the active call will be automatically held which will
// send another update at which point we will be in the right state.
if (mCallsManager.getActiveCall() != null
- && oldState == CallState.CONNECTING && newState == CallState.DIALING) {
+ && oldState == CallState.CONNECTING &&
+ (newState == CallState.DIALING || newState == CallState.PULLING)) {
return;
}
updateHeadsetWithCallState(false /* force */);
@@ -823,6 +824,7 @@
case CallState.CONNECTING:
case CallState.SELECT_PHONE_ACCOUNT:
case CallState.DIALING:
+ case CallState.PULLING:
// 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.
diff --git a/src/com/android/server/telecom/Call.java b/src/com/android/server/telecom/Call.java
index 0a967ac..1139cc5 100644
--- a/src/com/android/server/telecom/Call.java
+++ b/src/com/android/server/telecom/Call.java
@@ -677,6 +677,9 @@
case CallState.DIALING:
event = Log.Events.SET_DIALING;
break;
+ case CallState.PULLING:
+ event = Log.Events.SET_PULLING;
+ break;
case CallState.DISCONNECTED:
event = Log.Events.SET_DISCONNECTED;
data = getDisconnectCause();
@@ -2001,6 +2004,8 @@
return CallState.ACTIVE;
case Connection.STATE_DIALING:
return CallState.DIALING;
+ case Connection.STATE_PULLING_CALL:
+ return CallState.PULLING;
case Connection.STATE_DISCONNECTED:
return CallState.DISCONNECTED;
case Connection.STATE_HOLDING:
diff --git a/src/com/android/server/telecom/CallAudioManager.java b/src/com/android/server/telecom/CallAudioManager.java
index 0ff80f4..67003d8 100644
--- a/src/com/android/server/telecom/CallAudioManager.java
+++ b/src/com/android/server/telecom/CallAudioManager.java
@@ -72,6 +72,7 @@
put(CallState.CONNECTING, mActiveDialingOrConnectingCalls);
put(CallState.ACTIVE, mActiveDialingOrConnectingCalls);
put(CallState.DIALING, mActiveDialingOrConnectingCalls);
+ put(CallState.PULLING, mActiveDialingOrConnectingCalls);
put(CallState.RINGING, mRingingCalls);
put(CallState.ON_HOLD, mHoldingCalls);
}};
@@ -489,9 +490,13 @@
case CallState.ON_HOLD:
onCallLeavingHold();
break;
+ case CallState.PULLING:
+ onCallLeavingActiveDialingOrConnecting();
+ break;
case CallState.DIALING:
stopRingbackForCall(call);
onCallLeavingActiveDialingOrConnecting();
+ break;
}
}
@@ -507,6 +512,9 @@
case CallState.ON_HOLD:
onCallEnteringHold();
break;
+ case CallState.PULLING:
+ onCallEnteringActiveDialingOrConnecting();
+ break;
case CallState.DIALING:
onCallEnteringActiveDialingOrConnecting();
playRingbackForCall(call);
diff --git a/src/com/android/server/telecom/CallState.java b/src/com/android/server/telecom/CallState.java
index 0d2ca48..0aa928f 100644
--- a/src/com/android/server/telecom/CallState.java
+++ b/src/com/android/server/telecom/CallState.java
@@ -103,6 +103,15 @@
*/
public static final int DISCONNECTING = 9;
+ /**
+ * Indicates that the call is in the process of being pulled to the local device.
+ * <p>
+ * This state should only be set on a call with
+ * {@link android.telecom.Connection#PROPERTY_IS_EXTERNAL_CALL} and
+ * {@link android.telecom.Connection#CAPABILITY_CAN_PULL_CALL}.
+ */
+ public static final int PULLING = 10;
+
public static String toString(int callState) {
switch (callState) {
case NEW:
@@ -125,6 +134,8 @@
return "ABORTED";
case DISCONNECTING:
return "DISCONNECTING";
+ case PULLING:
+ return "PULLING";
default:
return "UNKNOWN";
}
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index c13c8a2..d3b23c2 100644
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -119,11 +119,13 @@
private static final int MAXIMUM_TOP_LEVEL_CALLS = 2;
private static final int[] OUTGOING_CALL_STATES =
- {CallState.CONNECTING, CallState.SELECT_PHONE_ACCOUNT, CallState.DIALING};
+ {CallState.CONNECTING, CallState.SELECT_PHONE_ACCOUNT, CallState.DIALING,
+ CallState.PULLING};
private static final int[] LIVE_CALL_STATES =
{CallState.CONNECTING, CallState.SELECT_PHONE_ACCOUNT, CallState.DIALING,
- CallState.ACTIVE};
+ CallState.PULLING, CallState.ACTIVE};
+
public static final String TELECOM_CALL_ID_PREFIX = "TC@";
// Maps call technologies in PhoneConstants to those in Analytics.
@@ -996,7 +998,8 @@
// STATE_DIALING, put it on hold before answering the call.
if (foregroundCall != null && foregroundCall != call &&
(foregroundCall.isActive() ||
- foregroundCall.getState() == CallState.DIALING)) {
+ foregroundCall.getState() == CallState.DIALING ||
+ foregroundCall.getState() == CallState.PULLING)) {
if (0 == (foregroundCall.getConnectionCapabilities()
& Connection.CAPABILITY_HOLD)) {
// This call does not support hold. If it is from a different connection
@@ -1357,6 +1360,11 @@
maybeMoveToSpeakerPhone(call);
}
+ void markCallAsPulling(Call call) {
+ setCallState(call, CallState.PULLING, "pulling set explicitly");
+ maybeMoveToSpeakerPhone(call);
+ }
+
void markCallAsActive(Call call) {
setCallState(call, CallState.ACTIVE, "active set explicitly");
maybeMoveToSpeakerPhone(call);
@@ -1450,7 +1458,8 @@
} else if (HeadsetMediaButton.LONG_PRESS == type) {
Log.d(this, "handleHeadsetHook: longpress -> hangup");
Call callToHangup = getFirstCallWithState(
- CallState.RINGING, CallState.DIALING, CallState.ACTIVE, CallState.ON_HOLD);
+ CallState.RINGING, CallState.DIALING, CallState.PULLING, CallState.ACTIVE,
+ CallState.ON_HOLD);
if (callToHangup != null) {
callToHangup.disconnect();
return true;
@@ -1480,6 +1489,9 @@
if (call.isEmergencyCall()) {
// We never support add call if one of the calls is an emergency call.
return false;
+ } else if (call.isExternalCall()) {
+ // External calls don't count.
+ continue;
} else if (call.getParentCall() == null) {
count++;
}
@@ -1850,7 +1862,7 @@
}
private boolean hasMaximumDialingCalls() {
- return MAXIMUM_DIALING_CALLS <= getNumCallsWithState(CallState.DIALING);
+ return MAXIMUM_DIALING_CALLS <= getNumCallsWithState(CallState.DIALING, CallState.PULLING);
}
private boolean makeRoomForOutgoingCall(Call call, boolean isEmergency) {
diff --git a/src/com/android/server/telecom/ConnectionServiceWrapper.java b/src/com/android/server/telecom/ConnectionServiceWrapper.java
index ca0e280..bf82a99 100644
--- a/src/com/android/server/telecom/ConnectionServiceWrapper.java
+++ b/src/com/android/server/telecom/ConnectionServiceWrapper.java
@@ -161,6 +161,24 @@
}
@Override
+ public void setPulling(String callId) {
+ Log.startSession(Log.Sessions.CSW_SET_PULLING);
+ long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ logIncoming("setPulling %s", callId);
+ Call call = mCallIdMapper.getCall(callId);
+ if (call != null) {
+ mCallsManager.markCallAsPulling(call);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ Log.endSession();
+ }
+ }
+
+ @Override
public void setDisconnected(String callId, DisconnectCause disconnectCause) {
Log.startSession(Log.Sessions.CSW_SET_DISCONNECTED);
long token = Binder.clearCallingIdentity();
diff --git a/src/com/android/server/telecom/CreateConnectionTimeout.java b/src/com/android/server/telecom/CreateConnectionTimeout.java
index 69cc129..9bfeb7f 100644
--- a/src/com/android/server/telecom/CreateConnectionTimeout.java
+++ b/src/com/android/server/telecom/CreateConnectionTimeout.java
@@ -114,7 +114,8 @@
int state = call.getState();
return state == CallState.NEW
|| state == CallState.CONNECTING
- || state == CallState.DIALING;
+ || state == CallState.DIALING
+ || state == CallState.PULLING;
}
private long getTimeoutLengthMillis() {
diff --git a/src/com/android/server/telecom/InCallController.java b/src/com/android/server/telecom/InCallController.java
index e0a9def..1432373 100644
--- a/src/com/android/server/telecom/InCallController.java
+++ b/src/com/android/server/telecom/InCallController.java
@@ -630,6 +630,7 @@
// Track the call if we don't already know about it.
addCall(call);
+ List<ComponentName> componentsUpdated = new ArrayList<>();
for (Map.Entry<InCallServiceInfo, IInCallService> entry : mInCallServices.entrySet()) {
InCallServiceInfo info = entry.getKey();
@@ -637,15 +638,18 @@
continue;
}
+ componentsUpdated.add(info.getComponentName());
IInCallService inCallService = entry.getValue();
ParcelableCall parcelableCall = ParcelableCallUtils.toParcelableCall(call,
- true /* includeVideoProvider */, mCallsManager.getPhoneAccountRegistrar());
+ true /* includeVideoProvider */, mCallsManager.getPhoneAccountRegistrar(),
+ info.isExternalCallsSupported());
try {
inCallService.addCall(parcelableCall);
} catch (RemoteException ignored) {
}
}
+ Log.i(this, "Call added to components: %s", componentsUpdated);
}
}
@@ -677,8 +681,62 @@
@Override
public void onExternalCallChanged(Call call, boolean isExternalCall) {
Log.i(this, "onExternalCallChanged: %s -> %b", call, isExternalCall);
- // TODO: Need to add logic which ensures changes to a call's external state adds or removes
- // the call from the InCallServices depending on whether they support external calls.
+
+ List<ComponentName> componentsUpdated = new ArrayList<>();
+ if (!isExternalCall) {
+ // The call was external but it is no longer external. We must now add it to any
+ // InCallServices which do not support external calls.
+ for (Map.Entry<InCallServiceInfo, IInCallService> entry : mInCallServices.entrySet()) {
+ InCallServiceInfo info = entry.getKey();
+
+ if (info.isExternalCallsSupported()) {
+ // For InCallServices which support external calls, the call will have already
+ // been added to the connection service, so we do not need to add it again.
+ continue;
+ }
+
+ componentsUpdated.add(info.getComponentName());
+ IInCallService inCallService = entry.getValue();
+
+ ParcelableCall parcelableCall = ParcelableCallUtils.toParcelableCall(call,
+ true /* includeVideoProvider */, mCallsManager.getPhoneAccountRegistrar(),
+ info.isExternalCallsSupported());
+ try {
+ inCallService.addCall(parcelableCall);
+ } catch (RemoteException ignored) {
+ }
+ }
+ Log.i(this, "Previously external call added to components: %s", componentsUpdated);
+ } else {
+ // The call was regular but it is now external. We must now remove it from any
+ // InCallServices which do not support external calls.
+ // Remove the call by sending a call update indicating the call was disconnected.
+ ParcelableCall parcelableCall = ParcelableCallUtils.toParcelableCall(
+ call,
+ false /* includeVideoProvider */,
+ mCallsManager.getPhoneAccountRegistrar(),
+ false /* supportsExternalCalls */,
+ android.telecom.Call.STATE_DISCONNECTED /* overrideState */);
+
+ Log.i(this, "Removing external call %s ==> %s", call, parcelableCall);
+ for (Map.Entry<InCallServiceInfo, IInCallService> entry : mInCallServices.entrySet()) {
+ InCallServiceInfo info = entry.getKey();
+ if (info.isExternalCallsSupported()) {
+ // For InCallServices which support external calls, we do not need to remove
+ // the call.
+ continue;
+ }
+
+ componentsUpdated.add(info.getComponentName());
+ IInCallService inCallService = entry.getValue();
+
+ try {
+ inCallService.updateCall(parcelableCall);
+ } catch (RemoteException ignored) {
+ }
+ }
+ Log.i(this, "External call removed from components: %s", componentsUpdated);
+ }
}
@Override
@@ -1032,7 +1090,8 @@
inCallService.addCall(ParcelableCallUtils.toParcelableCall(
call,
true /* includeVideoProvider */,
- mCallsManager.getPhoneAccountRegistrar()));
+ mCallsManager.getPhoneAccountRegistrar(),
+ info.isExternalCallsSupported()));
} catch (RemoteException ignored) {
}
}
@@ -1077,11 +1136,7 @@
*/
private void updateCall(Call call, boolean videoProviderChanged) {
if (!mInCallServices.isEmpty()) {
- ParcelableCall parcelableCall = ParcelableCallUtils.toParcelableCall(
- call,
- videoProviderChanged /* includeVideoProvider */,
- mCallsManager.getPhoneAccountRegistrar());
- Log.i(this, "Sending updateCall %s ==> %s", call, parcelableCall);
+ Log.i(this, "Sending updateCall %s", call);
List<ComponentName> componentsUpdated = new ArrayList<>();
for (Map.Entry<InCallServiceInfo, IInCallService> entry : mInCallServices.entrySet()) {
InCallServiceInfo info = entry.getKey();
@@ -1089,6 +1144,11 @@
continue;
}
+ ParcelableCall parcelableCall = ParcelableCallUtils.toParcelableCall(
+ call,
+ videoProviderChanged /* includeVideoProvider */,
+ mCallsManager.getPhoneAccountRegistrar(),
+ info.isExternalCallsSupported());
ComponentName componentName = info.getComponentName();
IInCallService inCallService = entry.getValue();
componentsUpdated.add(componentName);
diff --git a/src/com/android/server/telecom/Log.java b/src/com/android/server/telecom/Log.java
index 874b301..7f12404 100644
--- a/src/com/android/server/telecom/Log.java
+++ b/src/com/android/server/telecom/Log.java
@@ -68,6 +68,7 @@
public static final String CSW_SET_ACTIVE = "CSW.sA";
public static final String CSW_SET_RINGING = "CSW.sR";
public static final String CSW_SET_DIALING = "CSW.sD";
+ public static final String CSW_SET_PULLING = "CSW.sP";
public static final String CSW_SET_DISCONNECTED = "CSW.sDc";
public static final String CSW_SET_ON_HOLD = "CSW.sOH";
public static final String CSW_REMOVE_CALL = "CSW.rC";
@@ -108,6 +109,7 @@
public static final String DESTROYED = "DESTROYED";
public static final String SET_CONNECTING = "SET_CONNECTING";
public static final String SET_DIALING = "SET_DIALING";
+ public static final String SET_PULLING = "SET_PULLING";
public static final String SET_ACTIVE = "SET_ACTIVE";
public static final String SET_HOLD = "SET_HOLD";
public static final String SET_RINGING = "SET_RINGING";
diff --git a/src/com/android/server/telecom/ParcelableCallUtils.java b/src/com/android/server/telecom/ParcelableCallUtils.java
index 6f8b238..b64ce9c 100644
--- a/src/com/android/server/telecom/ParcelableCallUtils.java
+++ b/src/com/android/server/telecom/ParcelableCallUtils.java
@@ -28,11 +28,13 @@
* Utilities dealing with {@link ParcelableCall}.
*/
public class ParcelableCallUtils {
+ private static final int CALL_STATE_OVERRIDE_NONE = -1;
+
public static class Converter {
public ParcelableCall toParcelableCall(Call call, boolean includeVideoProvider,
PhoneAccountRegistrar phoneAccountRegistrar) {
return ParcelableCallUtils.toParcelableCall(
- call, includeVideoProvider, phoneAccountRegistrar);
+ call, includeVideoProvider, phoneAccountRegistrar, false);
}
}
@@ -45,13 +47,45 @@
* method creates a {@link VideoCallImpl} instance on access it is important for the
* recipient of the {@link ParcelableCall} to know if the video provider changed.
* @param phoneAccountRegistrar The {@link PhoneAccountRegistrar}.
+ * @param supportsExternalCalls Indicates whether the call should be parcelled for an
+ * {@link InCallService} which supports external calls or not.
+ */
+ public static ParcelableCall toParcelableCall(
+ Call call,
+ boolean includeVideoProvider,
+ PhoneAccountRegistrar phoneAccountRegistrar,
+ boolean supportsExternalCalls) {
+ return toParcelableCall(call, includeVideoProvider, phoneAccountRegistrar,
+ supportsExternalCalls, CALL_STATE_OVERRIDE_NONE /* overrideState */);
+ }
+
+ /**
+ * Parcels all information for a {@link Call} into a new {@link ParcelableCall} instance.
+ *
+ * @param call The {@link Call} to parcel.
+ * @param includeVideoProvider {@code true} if the video provider should be parcelled with the
+ * {@link Call}, {@code false} otherwise. Since the {@link ParcelableCall#getVideoCall()}
+ * method creates a {@link VideoCallImpl} instance on access it is important for the
+ * recipient of the {@link ParcelableCall} to know if the video provider changed.
+ * @param phoneAccountRegistrar The {@link PhoneAccountRegistrar}.
+ * @param supportsExternalCalls Indicates whether the call should be parcelled for an
+ * {@link InCallService} which supports external calls or not.
+ * @param overrideState When not {@link #CALL_STATE_OVERRIDE_NONE}, use the provided state as an
+ * override to whatever is defined in the call.
* @return The {@link ParcelableCall} containing all call information from the {@link Call}.
*/
public static ParcelableCall toParcelableCall(
Call call,
boolean includeVideoProvider,
- PhoneAccountRegistrar phoneAccountRegistrar) {
- int state = getParcelableState(call);
+ PhoneAccountRegistrar phoneAccountRegistrar,
+ boolean supportsExternalCalls,
+ int overrideState) {
+ int state;
+ if (overrideState == CALL_STATE_OVERRIDE_NONE) {
+ state = getParcelableState(call, supportsExternalCalls);
+ } else {
+ state = overrideState;
+ }
int capabilities = convertConnectionToCallCapabilities(call.getConnectionCapabilities());
int properties = convertConnectionToCallProperties(call.getConnectionProperties());
if (call.isConference()) {
@@ -141,7 +175,7 @@
call.getExtras());
}
- private static int getParcelableState(Call call) {
+ private static int getParcelableState(Call call, boolean supportsExternalCalls) {
int state = CallState.NEW;
switch (call.getState()) {
case CallState.ABORTED:
@@ -157,6 +191,19 @@
case CallState.DIALING:
state = android.telecom.Call.STATE_DIALING;
break;
+ case CallState.PULLING:
+ if (supportsExternalCalls) {
+ // The InCallService supports external calls, so it must handle
+ // STATE_PULLING_CALL.
+ state = android.telecom.Call.STATE_PULLING_CALL;
+ } else {
+ // The InCallService does NOT support external calls, so remap
+ // STATE_PULLING_CALL to STATE_DIALING. In essence, pulling a call can be seen
+ // as a form of dialing, so it is appropriate for InCallServices which do not
+ // handle external calls.
+ state = android.telecom.Call.STATE_DIALING;
+ }
+ break;
case CallState.DISCONNECTING:
state = android.telecom.Call.STATE_DISCONNECTING;
break;
diff --git a/src/com/android/server/telecom/PhoneStateBroadcaster.java b/src/com/android/server/telecom/PhoneStateBroadcaster.java
index b1f7e40..0781ca2 100644
--- a/src/com/android/server/telecom/PhoneStateBroadcaster.java
+++ b/src/com/android/server/telecom/PhoneStateBroadcaster.java
@@ -86,8 +86,8 @@
int callState = TelephonyManager.CALL_STATE_IDLE;
if (mCallsManager.hasRingingCall()) {
callState = TelephonyManager.CALL_STATE_RINGING;
- } else if (mCallsManager.getFirstCallWithState(CallState.DIALING, CallState.ACTIVE,
- CallState.ON_HOLD) != null) {
+ } else if (mCallsManager.getFirstCallWithState(CallState.DIALING, CallState.PULLING,
+ CallState.ACTIVE, CallState.ON_HOLD) != null) {
callState = TelephonyManager.CALL_STATE_OFFHOOK;
}
sendPhoneStateChangedBroadcast(call, callState);
diff --git a/src/com/android/server/telecom/TelecomServiceImpl.java b/src/com/android/server/telecom/TelecomServiceImpl.java
index ecc6e33..7dc0a79 100644
--- a/src/com/android/server/telecom/TelecomServiceImpl.java
+++ b/src/com/android/server/telecom/TelecomServiceImpl.java
@@ -1294,6 +1294,7 @@
call = mCallsManager.getFirstCallWithState(
CallState.ACTIVE,
CallState.DIALING,
+ CallState.PULLING,
CallState.RINGING,
CallState.ON_HOLD);
}
diff --git a/testapps/src/com/android/server/telecom/testapps/TestConnectionService.java b/testapps/src/com/android/server/telecom/testapps/TestConnectionService.java
index b30e372..0150dbe 100644
--- a/testapps/src/com/android/server/telecom/testapps/TestConnectionService.java
+++ b/testapps/src/com/android/server/telecom/testapps/TestConnectionService.java
@@ -165,6 +165,9 @@
capabilities |= CAPABILITY_RESPOND_VIA_TEXT;
setConnectionCapabilities(capabilities);
+ if (isIncoming) {
+ putExtra(Connection.EXTRA_ANSWERING_DROPS_FG_CALL, true);
+ }
LocalBroadcastManager.getInstance(getApplicationContext()).registerReceiver(
mHangupReceiver, new IntentFilter(TestCallActivity.ACTION_HANGUP_CALLS));
final IntentFilter filter =