Call handover improvements

- Set handover_from attribute when initiating a handover to another phone
account.  This lets the recipient know where the call is coming from.
- Replace "handover successful" indicator on calls with a handover state.
This ensures there is sufficient state to ensure that if a call is
hung up before the handover is accepted that we don't treat that hangup
as if the handover was successful.
- Ensure failed handovers are not logged.
- Prevent EXTRA_IS_HANDOVER from being set for outgoing calls.

Test: Unit, Manual
Bug: 37977155
Bug: 38027075
Change-Id: Ie5240c28163714be71825e718daa560eb568330f
diff --git a/src/com/android/server/telecom/Call.java b/src/com/android/server/telecom/Call.java
index 2bf05ae..98d3dd3 100644
--- a/src/com/android/server/telecom/Call.java
+++ b/src/com/android/server/telecom/Call.java
@@ -456,20 +456,18 @@
      * When a call handover has been initiated via {@link #requestHandover(PhoneAccountHandle,
      * int, Bundle)}, contains the call which this call is being handed over to.
      */
-    private Call mHandoverToCall = null;
+    private Call mHandoverDestinationCall = null;
 
     /**
      * When a call handover has been initiated via {@link #requestHandover(PhoneAccountHandle,
      * int, Bundle)}, contains the call which this call is being handed over from.
      */
-    private Call mHandoverFromCall = null;
+    private Call mHandoverSourceCall = null;
 
     /**
-     * When a call handover has been initiated via {@link #requestHandover(PhoneAccountHandle,
-     * int, Bundle)} and the handover has successfully succeeded, this field is set {@code true} to
-     * indicate that the call was handed over from another call.
+     * Indicates the current state of this call if it is in the process of a handover.
      */
-    private boolean mIsHandoverSuccessful = false;
+    private int mHandoverState = HandoverState.HANDOVER_NONE;
 
     /**
      * Persists the specified parameters and initializes the new instance.
@@ -1091,57 +1089,56 @@
         setConnectionProperties(getConnectionProperties());
     }
 
-    /**
-     * Marks a handover as failed.
-     */
-    public void markHandoverFailed() {
-        markHandoverResult(false /* isComplete */);
-    }
-
-    /**
-     * Marks a handover as being successful.
-     */
-    public void markHandoverSuccess() {
-       markHandoverResult(true /* isComplete */);
-    }
-
-    private void markHandoverResult(boolean isHandoverSuccessful) {
-        if (mHandoverFromCall != null) {
-            mHandoverFromCall.mIsHandoverSuccessful = isHandoverSuccessful;
-            mHandoverFromCall.setHandoverFromCall(null);
-            mHandoverFromCall.setHandoverToCall(null);
-            mHandoverFromCall = null;
-        } else if (mHandoverToCall != null) {
-            mHandoverToCall.mIsHandoverSuccessful = isHandoverSuccessful;
-            mHandoverToCall.setHandoverFromCall(null);
-            mHandoverToCall.setHandoverToCall(null);
-            mHandoverToCall = null;
+    public void markFinishedHandoverStateAndCleanup(int handoverState) {
+        if (mHandoverSourceCall != null) {
+            mHandoverSourceCall.setHandoverState(handoverState);
+        } else if (mHandoverDestinationCall != null) {
+            mHandoverDestinationCall.setHandoverState(handoverState);
         }
-        mIsHandoverSuccessful = isHandoverSuccessful;
+        setHandoverState(handoverState);
+        maybeCleanupHandover();
+    }
+
+    public void maybeCleanupHandover() {
+        if (mHandoverSourceCall != null) {
+            mHandoverSourceCall.setHandoverSourceCall(null);
+            mHandoverSourceCall.setHandoverDestinationCall(null);
+            mHandoverSourceCall = null;
+        } else if (mHandoverDestinationCall != null) {
+            mHandoverDestinationCall.setHandoverSourceCall(null);
+            mHandoverDestinationCall.setHandoverDestinationCall(null);
+            mHandoverDestinationCall = null;
+        }
     }
 
     public boolean isHandoverInProgress() {
-        return mHandoverFromCall != null || mHandoverToCall != null;
+        return mHandoverSourceCall != null || mHandoverDestinationCall != null;
     }
 
-    public Call getHandoverToCall() {
-        return mHandoverToCall;
+    public Call getHandoverDestinationCall() {
+        return mHandoverDestinationCall;
     }
 
-    public void setHandoverToCall(Call call) {
-        mHandoverToCall = call;
+    public void setHandoverDestinationCall(Call call) {
+        mHandoverDestinationCall = call;
     }
 
-    public Call getHandoverFromCall() {
-        return mHandoverFromCall;
+    public Call getHandoverSourceCall() {
+        return mHandoverSourceCall;
     }
 
-    public void setHandoverFromCall(Call call) {
-        mHandoverFromCall = call;
+    public void setHandoverSourceCall(Call call) {
+        mHandoverSourceCall = call;
     }
 
-    public boolean isHandoverSuccessful() {
-        return mIsHandoverSuccessful;
+    public void setHandoverState(int handoverState) {
+        Log.d(this, "setHandoverState: callId=%s, handoverState=%s", getId(),
+                HandoverState.stateToString(handoverState));
+        mHandoverState = handoverState;
+    }
+
+    public int getHandoverState() {
+        return mHandoverState;
     }
 
     private void configureIsWorkCall() {
diff --git a/src/com/android/server/telecom/CallLogManager.java b/src/com/android/server/telecom/CallLogManager.java
index 8057afc..0ff258e 100755
--- a/src/com/android/server/telecom/CallLogManager.java
+++ b/src/com/android/server/telecom/CallLogManager.java
@@ -153,7 +153,8 @@
                  !isCallCanceled) &&
                 !call.isExternalCall() &&
                 (!call.isSelfManaged() ||
-                call.isLoggedSelfManaged())) {
+                call.isLoggedSelfManaged() &&
+                        call.getHandoverState() != HandoverState.HANDOVER_FAILED)) {
             int type;
             if (!call.isIncoming()) {
                 type = Calls.OUTGOING_TYPE;
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index 99e9e03..8a03f24 100644
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -884,7 +884,6 @@
             getForegroundCall().getAnalytics().setCallIsInterrupted(true);
             call.getAnalytics().setCallIsAdditional(true);
         }
-
         setIntentExtrasAndStartTime(call, extras);
         // TODO: Move this to be a part of addCall()
         call.addListener(this);
@@ -902,7 +901,7 @@
                 if (fromCall != null) {
                     if (!isHandoverFromPhoneAccountSupported(fromCall.getTargetPhoneAccount())) {
                         Log.w(this, "processIncomingCallIntent: From account doesn't support " +
-                                        "handover.");
+                                "handover.");
                         isHandoverAllowed = false;
                     }
                 } else {
@@ -912,8 +911,10 @@
 
                 if (isHandoverAllowed) {
                     // Link the calls so we know we're handing over.
-                    fromCall.setHandoverToCall(call);
-                    call.setHandoverFromCall(fromCall);
+                    fromCall.setHandoverDestinationCall(call);
+                    call.setHandoverSourceCall(fromCall);
+                    call.setHandoverState(HandoverState.HANDOVER_TO_STARTED);
+                    fromCall.setHandoverState(HandoverState.HANDOVER_FROM_STARTED);
                     Log.addEvent(fromCall, LogUtils.Events.START_HANDOVER,
                             "handOverFrom=%s, handOverTo=%s", fromCall.getId(), call.getId());
                     Log.addEvent(call, LogUtils.Events.START_HANDOVER,
@@ -921,9 +922,9 @@
                 }
             } else {
                 Log.w(this, "processIncomingCallIntent: To account doesn't support handover.");
-                isHandoverAllowed = false;
             }
         }
+
         if (!isHandoverAllowed || (call.isSelfManaged() && !isIncomingCallPermitted(call,
                 call.getTargetPhoneAccount()))) {
             notifyCreateConnectionFailed(phoneAccountHandle, call);
@@ -1721,8 +1722,7 @@
      * Removes an existing disconnected call, and notifies the in-call app.
      */
     void markCallAsRemoved(Call call) {
-        call.markHandoverFailed();
-
+        call.maybeCleanupHandover();
         removeCall(call);
         Call foregroundCall = mCallAudioManager.getPossiblyHeldForegroundCall();
         if (mLocallyDisconnectingCalls.contains(call)) {
@@ -2177,60 +2177,7 @@
 
             Trace.beginSection("onCallStateChanged");
 
-            // If this call became active because it is being handed over from another Call, the
-            // call which was being handed over from can be disconnected at this point.
-            if (call.getHandoverFromCall() != null) {
-                if (newState == CallState.ACTIVE) {
-                    Call handoverFrom = call.getHandoverFromCall();
-                    Log.addEvent(call, LogUtils.Events.HANDOVER_COMPLETE, "from=%s, to=%s",
-                            handoverFrom.getId(), call.getId());
-                    Log.addEvent(handoverFrom, LogUtils.Events.HANDOVER_COMPLETE, "from=%s, to=%s",
-                            handoverFrom.getId(), call.getId());
-                    handoverFrom.onConnectionEvent(
-                            android.telecom.Connection.EVENT_HANDOVER_COMPLETE, null);
-                    markCallAsDisconnected(handoverFrom,
-                            new DisconnectCause(DisconnectCause.LOCAL));
-                    call.markHandoverSuccess();
-                    markCallAsRemoved(handoverFrom);
-                    call.sendCallEvent(android.telecom.Call.EVENT_HANDOVER_COMPLETE, null);
-                } else if (newState == CallState.DISCONNECTED) {
-                    Call handoverFrom = call.getHandoverFromCall();
-                    Log.i(this, "Call %s failed to handover from %s.",
-                            call.getId(), handoverFrom.getId());
-                    Log.addEvent(handoverFrom, LogUtils.Events.HANDOVER_FAILED, "from=%s, to=%s",
-                            call.getId(), handoverFrom.getId());
-                    // Inform the "from" Call (ie the source call) that the handover from it has
-                    // failed; this allows the InCallService to be notified that a handover it
-                    // initiated failed.
-                    handoverFrom.onConnectionEvent(Connection.EVENT_HANDOVER_FAILED, null);
-                    // Inform the "to" ConnectionService that handover to it has failed.  This
-                    // allows the ConnectionService the call was being handed over
-                    if (call.getConnectionService() != null) {
-                        // Only attempt if the call has a bound ConnectionService if handover failed
-                        // early on in the handover process, the CS will be unbound and we won't be
-                        // able to send the call event.
-                        call.sendCallEvent(android.telecom.Call.EVENT_HANDOVER_FAILED, null);
-                    }
-                    call.markHandoverFailed();
-                }
-            // If this call was disconnected because it was handed over TO another call, report the
-            // handover as complete.
-            } else if (call.getHandoverToCall() != null && newState == CallState.DISCONNECTED) {
-                Call handoverTo = call.getHandoverToCall();
-                Log.addEvent(handoverTo, LogUtils.Events.HANDOVER_COMPLETE, "from=%s, to=%s",
-                        call.getId(), handoverTo.getId());
-                Log.addEvent(call, LogUtils.Events.HANDOVER_COMPLETE, "from=%s, to=%s",
-                        call.getId(), handoverTo.getId());
-
-                // Inform the "from" Call (ie the source call) that the handover from it has
-                // completed; this allows the InCallService to be notified that a handover it
-                // initiated completed.
-                call.onConnectionEvent(Connection.EVENT_HANDOVER_COMPLETE, null);
-                // Inform the "to" ConnectionService that handover to it has completed.
-                handoverTo.sendCallEvent(android.telecom.Call.EVENT_HANDOVER_COMPLETE, null);
-                answerCall(handoverTo, handoverTo.getVideoState());
-                call.markHandoverSuccess();
-            }
+            maybeHandleHandover(call, newState);
 
             // Only broadcast state change for calls that are being tracked.
             if (mCalls.contains(call)) {
@@ -2249,6 +2196,105 @@
         }
     }
 
+    /**
+     * Identifies call state transitions for a call which trigger handover events.
+     * - If this call has a handover to it which just started and this call goes active, treat
+     * this as if the user accepted the handover.
+     * - If this call has a handover to it which just started and this call is disconnected, treat
+     * this as if the user rejected the handover.
+     * - If this call has a handover from it which just started and this call is disconnected, do
+     * nothing as the call prematurely disconnected before the user accepted the handover.
+     * - If this call has a handover from it which was already accepted by the user and this call is
+     * disconnected, mark the handover as complete.
+     *
+     * @param call A call whose state is changing.
+     * @param newState The new state of the call.
+     */
+    private void maybeHandleHandover(Call call, int newState) {
+        if (call.getHandoverSourceCall() != null) {
+            // We are handing over another call to this one.
+            if (call.getHandoverState() == HandoverState.HANDOVER_TO_STARTED) {
+                // A handover to this call has just been initiated.
+                if (newState == CallState.ACTIVE) {
+                    // This call went active, so the user has accepted the handover.
+                    Log.i(this, "setCallState: handover to accepted");
+                    acceptHandoverTo(call);
+                } else if (newState == CallState.DISCONNECTED) {
+                    // The call was disconnected, so the user has rejected the handover.
+                    Log.i(this, "setCallState: handover to rejected");
+                    rejectHandoverTo(call);
+                }
+            }
+        // If this call was disconnected because it was handed over TO another call, report the
+        // handover as complete.
+        } else if (call.getHandoverDestinationCall() != null
+                && newState == CallState.DISCONNECTED) {
+            int handoverState = call.getHandoverState();
+            if (handoverState == HandoverState.HANDOVER_FROM_STARTED) {
+                // Disconnect before handover was accepted.
+                Log.i(this, "setCallState: disconnect before handover accepted");
+            } else if (handoverState == HandoverState.HANDOVER_ACCEPTED) {
+                Log.i(this, "setCallState: handover from complete");
+                completeHandoverFrom(call);
+            }
+        }
+    }
+
+    private void completeHandoverFrom(Call call) {
+        Call handoverTo = call.getHandoverDestinationCall();
+        Log.addEvent(handoverTo, LogUtils.Events.HANDOVER_COMPLETE, "from=%s, to=%s",
+                call.getId(), handoverTo.getId());
+        Log.addEvent(call, LogUtils.Events.HANDOVER_COMPLETE, "from=%s, to=%s",
+                call.getId(), handoverTo.getId());
+
+        // Inform the "from" Call (ie the source call) that the handover from it has
+        // completed; this allows the InCallService to be notified that a handover it
+        // initiated completed.
+        call.onConnectionEvent(Connection.EVENT_HANDOVER_COMPLETE, null);
+        // Inform the "to" ConnectionService that handover to it has completed.
+        handoverTo.sendCallEvent(android.telecom.Call.EVENT_HANDOVER_COMPLETE, null);
+        answerCall(handoverTo, handoverTo.getVideoState());
+        call.markFinishedHandoverStateAndCleanup(HandoverState.HANDOVER_COMPLETE);
+    }
+
+    private void rejectHandoverTo(Call handoverTo) {
+        Call handoverFrom = handoverTo.getHandoverSourceCall();
+        Log.i(this, "rejectHandoverTo: from=%s, to=%s", handoverFrom.getId(), handoverTo.getId());
+        Log.addEvent(handoverFrom, LogUtils.Events.HANDOVER_FAILED, "from=%s, to=%s",
+                handoverTo.getId(), handoverFrom.getId());
+        Log.addEvent(handoverTo, LogUtils.Events.HANDOVER_FAILED, "from=%s, to=%s",
+                handoverTo.getId(), handoverFrom.getId());
+
+        // Inform the "from" Call (ie the source call) that the handover from it has
+        // failed; this allows the InCallService to be notified that a handover it
+        // initiated failed.
+        handoverFrom.onConnectionEvent(Connection.EVENT_HANDOVER_FAILED, null);
+        // Inform the "to" ConnectionService that handover to it has failed.  This
+        // allows the ConnectionService the call was being handed over
+        if (handoverTo.getConnectionService() != null) {
+            // Only attempt if the call has a bound ConnectionService if handover failed
+            // early on in the handover process, the CS will be unbound and we won't be
+            // able to send the call event.
+            handoverTo.sendCallEvent(android.telecom.Call.EVENT_HANDOVER_FAILED, null);
+        }
+        handoverTo.markFinishedHandoverStateAndCleanup(HandoverState.HANDOVER_FAILED);
+    }
+
+    private void acceptHandoverTo(Call handoverTo) {
+        Call handoverFrom = handoverTo.getHandoverSourceCall();
+        Log.i(this, "acceptHandoverTo: from=%s, to=%s", handoverFrom.getId(), handoverTo.getId());
+        handoverTo.setHandoverState(HandoverState.HANDOVER_ACCEPTED);
+        handoverFrom.setHandoverState(HandoverState.HANDOVER_ACCEPTED);
+
+        Log.addEvent(handoverTo, LogUtils.Events.ACCEPT_HANDOVER, "from=%s, to=%s",
+                handoverFrom.getId(), handoverTo.getId());
+        Log.addEvent(handoverFrom, LogUtils.Events.ACCEPT_HANDOVER, "from=%s, to=%s",
+                handoverFrom.getId(), handoverTo.getId());
+
+        // Disconnect the call we handed over from.
+        disconnectCall(handoverFrom);
+    }
+
     private void updateCanAddCall() {
         boolean newCanAddCall = canAddCall();
         if (newCanAddCall != mCanAddCall) {
@@ -2455,7 +2501,7 @@
     public boolean shouldShowSystemIncomingCallUi(Call incomingCall) {
         return incomingCall.isIncoming() && incomingCall.isSelfManaged() &&
                 hasCallsForOtherPhoneAccount(incomingCall.getTargetPhoneAccount()) &&
-                incomingCall.getHandoverFromCall() == null;
+                incomingCall.getHandoverSourceCall() == null;
     }
 
     private boolean makeRoomForOutgoingCall(Call call, boolean isEmergency) {
@@ -2785,7 +2831,7 @@
             // Only permit outgoing calls if there is no ongoing emergency calls and all other calls
             // are associated with the current PhoneAccountHandle.
             return !hasEmergencyCall() && (
-                    excludeCall.getHandoverFromCall() != null ||
+                    excludeCall.getHandoverSourceCall() != null ||
                             (!hasMaximumSelfManagedCalls(excludeCall, phoneAccountHandle) &&
                             !hasCallsForOtherPhoneAccount(phoneAccountHandle) &&
                             !hasManagedCalls()));
@@ -3046,6 +3092,8 @@
 
         Bundle extras = new Bundle();
         extras.putBoolean(TelecomManager.EXTRA_IS_HANDOVER, true);
+        extras.putParcelable(TelecomManager.EXTRA_HANDOVER_FROM_PHONE_ACCOUNT,
+                handoverFromCall.getTargetPhoneAccount());
         extras.putInt(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, videoState);
         if (initiatingExtras != null) {
             extras.putAll(initiatingExtras);
@@ -3056,8 +3104,10 @@
                 extras, getCurrentUserHandle(), null /* originalIntent */);
         Log.addEvent(handoverFromCall, LogUtils.Events.START_HANDOVER,
                 "handOverFrom=%s, handOverTo=%s", handoverFromCall.getId(), handoverToCall.getId());
-        handoverFromCall.setHandoverToCall(handoverToCall);
-        handoverToCall.setHandoverFromCall(handoverFromCall);
+        handoverFromCall.setHandoverDestinationCall(handoverToCall);
+        handoverFromCall.setHandoverState(HandoverState.HANDOVER_FROM_STARTED);
+        handoverToCall.setHandoverState(HandoverState.HANDOVER_TO_STARTED);
+        handoverToCall.setHandoverSourceCall(handoverFromCall);
         handoverToCall.setNewOutgoingCallIntentBroadcastIsDone();
         placeOutgoingCall(handoverToCall, handoverToCall.getHandle(), null /* gatewayInfo */,
                 false /* startwithSpeaker */,
@@ -3109,8 +3159,8 @@
      * @return {@code true} if a call in the process of handover exists, {@code false} otherwise.
      */
     private boolean isHandoverInProgress() {
-        return mCalls.stream().filter(c -> c.getHandoverFromCall() != null ||
-                c.getHandoverToCall() != null).count() > 0;
+        return mCalls.stream().filter(c -> c.getHandoverSourceCall() != null ||
+                c.getHandoverDestinationCall() != null).count() > 0;
     }
 
     private void broadcastUnregisterIntent(PhoneAccountHandle accountHandle) {
diff --git a/src/com/android/server/telecom/ConnectionServiceWrapper.java b/src/com/android/server/telecom/ConnectionServiceWrapper.java
index d9be775..8f54cec 100644
--- a/src/com/android/server/telecom/ConnectionServiceWrapper.java
+++ b/src/com/android/server/telecom/ConnectionServiceWrapper.java
@@ -930,8 +930,10 @@
 
                 // Call is incoming and added because we're handing over from another; tell CS
                 // that its expected to handover.
-                if (call.isIncoming() && call.getHandoverFromCall() != null) {
+                if (call.isIncoming() && call.getHandoverSourceCall() != null) {
                     extras.putBoolean(TelecomManager.EXTRA_IS_HANDOVER, true);
+                    extras.putParcelable(TelecomManager.EXTRA_HANDOVER_FROM_PHONE_ACCOUNT,
+                            call.getHandoverSourceCall().getTargetPhoneAccount());
                 }
 
                 Log.addEvent(call, LogUtils.Events.START_CONNECTION,
diff --git a/src/com/android/server/telecom/HandoverState.java b/src/com/android/server/telecom/HandoverState.java
new file mode 100644
index 0000000..c68f0d3
--- /dev/null
+++ b/src/com/android/server/telecom/HandoverState.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.telecom;
+
+/**
+ * Defines handover state constants for calls undergoing handover.
+ */
+public class HandoverState {
+    private HandoverState() {
+        // Can't instantiate.
+    }
+
+    public static final int HANDOVER_NONE = 1;
+    public static final int HANDOVER_TO_STARTED = 2;
+    public static final int HANDOVER_FROM_STARTED = 3;
+    public static final int HANDOVER_ACCEPTED = 4;
+    public static final int HANDOVER_COMPLETE = 5;
+    public static final int HANDOVER_FAILED = 6;
+
+    private static final String HANDOVER_NONE_STR = "NONE";
+    private static final String HANDOVER_TO_STARTED_STR = "HANDOVER_TO_STARTED";
+    private static final String HANDOVER_FROM_STARTED_STR = "HANDOVER_FROM_STARTED";
+    private static final String HANDOVER_ACCEPTED_STR = "HANDOVER_ACCEPTED";
+    private static final String HANDOVER_COMPLETE_STR = "HANDOVER_COMPLETE";
+    private static final String HANDOVER_FAILED_STR = "HANDOVER_FAILED";
+
+    public static String stateToString(int state) {
+        switch (state) {
+            case HANDOVER_NONE:
+                return HANDOVER_NONE_STR;
+            case HANDOVER_TO_STARTED:
+                return HANDOVER_TO_STARTED_STR;
+            case HANDOVER_FROM_STARTED:
+                return HANDOVER_FROM_STARTED_STR;
+            case HANDOVER_ACCEPTED:
+                return HANDOVER_ACCEPTED_STR;
+            case HANDOVER_COMPLETE:
+                return HANDOVER_COMPLETE_STR;
+            case HANDOVER_FAILED:
+                return HANDOVER_FAILED_STR;
+        }
+        return "";
+    }
+}
diff --git a/src/com/android/server/telecom/LogUtils.java b/src/com/android/server/telecom/LogUtils.java
index 34d875e..0411355 100644
--- a/src/com/android/server/telecom/LogUtils.java
+++ b/src/com/android/server/telecom/LogUtils.java
@@ -129,6 +129,7 @@
         public static final String CALL_EVENT = "CALL_EVENT";
         public static final String HANDOVER_REQUEST = "HANDOVER_REQUEST";
         public static final String START_HANDOVER = "START_HANDOVER";
+        public static final String ACCEPT_HANDOVER = "ACCEPT_HANDOVER";
         public static final String HANDOVER_COMPLETE = "HANDOVER_COMPLETE";
         public static final String HANDOVER_FAILED = "HANDOVER_FAILED";
 
diff --git a/src/com/android/server/telecom/TelecomServiceImpl.java b/src/com/android/server/telecom/TelecomServiceImpl.java
index cbcd41d..3a09aa8 100644
--- a/src/com/android/server/telecom/TelecomServiceImpl.java
+++ b/src/com/android/server/telecom/TelecomServiceImpl.java
@@ -1083,6 +1083,10 @@
                 if (extras != null) {
                     phoneAccountHandle = extras.getParcelable(
                             TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE);
+                    if (extras.containsKey(TelecomManager.EXTRA_IS_HANDOVER)) {
+                        // This extra is for Telecom use only so should never be passed in.
+                        extras.remove(TelecomManager.EXTRA_IS_HANDOVER);
+                    }
                 }
                 boolean isSelfManaged = phoneAccountHandle != null &&
                         isSelfManagedConnectionService(phoneAccountHandle);
diff --git a/src/com/android/server/telecom/ui/IncomingCallNotifier.java b/src/com/android/server/telecom/ui/IncomingCallNotifier.java
index 0d281a5..0c90e06 100644
--- a/src/com/android/server/telecom/ui/IncomingCallNotifier.java
+++ b/src/com/android/server/telecom/ui/IncomingCallNotifier.java
@@ -36,6 +36,7 @@
 import com.android.server.telecom.Call;
 import com.android.server.telecom.CallState;
 import com.android.server.telecom.CallsManagerListenerBase;
+import com.android.server.telecom.HandoverState;
 import com.android.server.telecom.R;
 import com.android.server.telecom.TelecomBroadcastIntentProcessor;
 import com.android.server.telecom.components.TelecomBroadcastReceiver;
@@ -131,8 +132,8 @@
     private void updateIncomingCall() {
         Optional<Call> incomingCallOp = mCalls.stream()
                 .filter(call -> call.isSelfManaged() && call.isIncoming() &&
-                        call.getState() == CallState.RINGING && !call.isHandoverInProgress() &&
-                        !call.isHandoverSuccessful())
+                        call.getState() == CallState.RINGING &&
+                        call.getHandoverState() == HandoverState.HANDOVER_NONE)
                 .findFirst();
         Call incomingCall = incomingCallOp.orElse(null);
         if (incomingCall != null && mCallsManagerProxy != null &&
diff --git a/testapps/src/com/android/server/telecom/testapps/SelfManagedConnectionService.java b/testapps/src/com/android/server/telecom/testapps/SelfManagedConnectionService.java
index 7ef9c26..bb34530 100644
--- a/testapps/src/com/android/server/telecom/testapps/SelfManagedConnectionService.java
+++ b/testapps/src/com/android/server/telecom/testapps/SelfManagedConnectionService.java
@@ -82,6 +82,9 @@
         }
         Bundle requestExtras = request.getExtras();
         if (requestExtras != null) {
+            Log.i(this, "createConnection: isHandover=%b, handoverFrom=%s",
+                    requestExtras.getBoolean(TelecomManager.EXTRA_IS_HANDOVER),
+                    requestExtras.getString(TelecomManager.EXTRA_HANDOVER_FROM_PHONE_ACCOUNT));
             connection.setIsHandover(requestExtras.getBoolean(TelecomManager.EXTRA_IS_HANDOVER,
                     false));
             if (!isIncoming && connection.isHandover()) {
diff --git a/tests/src/com/android/server/telecom/tests/IncomingCallNotifierTest.java b/tests/src/com/android/server/telecom/tests/IncomingCallNotifierTest.java
index b320985..1909259 100644
--- a/tests/src/com/android/server/telecom/tests/IncomingCallNotifierTest.java
+++ b/tests/src/com/android/server/telecom/tests/IncomingCallNotifierTest.java
@@ -25,6 +25,7 @@
 
 import com.android.server.telecom.Call;
 import com.android.server.telecom.CallState;
+import com.android.server.telecom.HandoverState;
 import com.android.server.telecom.ui.IncomingCallNotifier;
 
 import org.mockito.Mock;
@@ -70,8 +71,7 @@
         when(mRingingCall.getState()).thenReturn(CallState.RINGING);
         when(mRingingCall.getVideoState()).thenReturn(VideoProfile.STATE_AUDIO_ONLY);
         when(mRingingCall.getTargetPhoneAccountLabel()).thenReturn("Foo");
-        when(mRingingCall.isHandoverInProgress()).thenReturn(false);
-        when(mRingingCall.isHandoverSuccessful()).thenReturn(false);
+        when(mRingingCall.getHandoverState()).thenReturn(HandoverState.HANDOVER_NONE);
     }
 
     /**
@@ -136,8 +136,7 @@
         when(mCallsManagerProxy.hasCallsForOtherPhoneAccount(any())).thenReturn(true);
         when(mCallsManagerProxy.getNumCallsForOtherPhoneAccount(any())).thenReturn(1);
         when(mCallsManagerProxy.getActiveCall()).thenReturn(mAudioCall);
-        when(mRingingCall.isHandoverInProgress()).thenReturn(true);
-        when(mRingingCall.isHandoverSuccessful()).thenReturn(false);
+        when(mRingingCall.getHandoverState()).thenReturn(HandoverState.HANDOVER_FROM_STARTED);
 
         mIncomingCallNotifier.onCallAdded(mAudioCall);
         mIncomingCallNotifier.onCallAdded(mRingingCall);
@@ -155,8 +154,7 @@
         when(mCallsManagerProxy.hasCallsForOtherPhoneAccount(any())).thenReturn(true);
         when(mCallsManagerProxy.getNumCallsForOtherPhoneAccount(any())).thenReturn(1);
         when(mCallsManagerProxy.getActiveCall()).thenReturn(mAudioCall);
-        when(mRingingCall.isHandoverInProgress()).thenReturn(false);
-        when(mRingingCall.isHandoverSuccessful()).thenReturn(true);
+        when(mRingingCall.getHandoverState()).thenReturn(HandoverState.HANDOVER_COMPLETE);
 
         mIncomingCallNotifier.onCallAdded(mAudioCall);
         mIncomingCallNotifier.onCallAdded(mRingingCall);