Loading src/com/android/server/telecom/Call.java +38 −41 Original line number Original line Diff line number Diff line Loading @@ -456,20 +456,18 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable { * When a call handover has been initiated via {@link #requestHandover(PhoneAccountHandle, * When a call handover has been initiated via {@link #requestHandover(PhoneAccountHandle, * int, Bundle)}, contains the call which this call is being handed over to. * 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, * When a call handover has been initiated via {@link #requestHandover(PhoneAccountHandle, * int, Bundle)}, contains the call which this call is being handed over from. * 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, * Indicates the current state of this call if it is in the process of a handover. * 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. */ */ private boolean mIsHandoverSuccessful = false; private int mHandoverState = HandoverState.HANDOVER_NONE; /** /** * Persists the specified parameters and initializes the new instance. * Persists the specified parameters and initializes the new instance. Loading Loading @@ -1091,57 +1089,56 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable { setConnectionProperties(getConnectionProperties()); setConnectionProperties(getConnectionProperties()); } } /** public void markFinishedHandoverStateAndCleanup(int handoverState) { * Marks a handover as failed. if (mHandoverSourceCall != null) { */ mHandoverSourceCall.setHandoverState(handoverState); public void markHandoverFailed() { } else if (mHandoverDestinationCall != null) { markHandoverResult(false /* isComplete */); mHandoverDestinationCall.setHandoverState(handoverState); } } setHandoverState(handoverState); /** maybeCleanupHandover(); * Marks a handover as being successful. */ public void markHandoverSuccess() { markHandoverResult(true /* isComplete */); } } private void markHandoverResult(boolean isHandoverSuccessful) { public void maybeCleanupHandover() { if (mHandoverFromCall != null) { if (mHandoverSourceCall != null) { mHandoverFromCall.mIsHandoverSuccessful = isHandoverSuccessful; mHandoverSourceCall.setHandoverSourceCall(null); mHandoverFromCall.setHandoverFromCall(null); mHandoverSourceCall.setHandoverDestinationCall(null); mHandoverFromCall.setHandoverToCall(null); mHandoverSourceCall = null; mHandoverFromCall = null; } else if (mHandoverDestinationCall != null) { } else if (mHandoverToCall != null) { mHandoverDestinationCall.setHandoverSourceCall(null); mHandoverToCall.mIsHandoverSuccessful = isHandoverSuccessful; mHandoverDestinationCall.setHandoverDestinationCall(null); mHandoverToCall.setHandoverFromCall(null); mHandoverDestinationCall = null; mHandoverToCall.setHandoverToCall(null); mHandoverToCall = null; } } mIsHandoverSuccessful = isHandoverSuccessful; } } public boolean isHandoverInProgress() { public boolean isHandoverInProgress() { return mHandoverFromCall != null || mHandoverToCall != null; return mHandoverSourceCall != null || mHandoverDestinationCall != null; } public Call getHandoverDestinationCall() { return mHandoverDestinationCall; } } public Call getHandoverToCall() { public void setHandoverDestinationCall(Call call) { return mHandoverToCall; mHandoverDestinationCall = call; } } public void setHandoverToCall(Call call) { public Call getHandoverSourceCall() { mHandoverToCall = call; return mHandoverSourceCall; } } public Call getHandoverFromCall() { public void setHandoverSourceCall(Call call) { return mHandoverFromCall; mHandoverSourceCall = call; } } public void setHandoverFromCall(Call call) { public void setHandoverState(int handoverState) { mHandoverFromCall = call; Log.d(this, "setHandoverState: callId=%s, handoverState=%s", getId(), HandoverState.stateToString(handoverState)); mHandoverState = handoverState; } } public boolean isHandoverSuccessful() { public int getHandoverState() { return mIsHandoverSuccessful; return mHandoverState; } } private void configureIsWorkCall() { private void configureIsWorkCall() { Loading src/com/android/server/telecom/CallLogManager.java +2 −1 Original line number Original line Diff line number Diff line Loading @@ -153,7 +153,8 @@ public final class CallLogManager extends CallsManagerListenerBase { !isCallCanceled) && !isCallCanceled) && !call.isExternalCall() && !call.isExternalCall() && (!call.isSelfManaged() || (!call.isSelfManaged() || call.isLoggedSelfManaged())) { call.isLoggedSelfManaged() && call.getHandoverState() != HandoverState.HANDOVER_FAILED)) { int type; int type; if (!call.isIncoming()) { if (!call.isIncoming()) { type = Calls.OUTGOING_TYPE; type = Calls.OUTGOING_TYPE; Loading src/com/android/server/telecom/CallsManager.java +117 −67 Original line number Original line Diff line number Diff line Loading @@ -884,7 +884,6 @@ public class CallsManager extends Call.ListenerBase getForegroundCall().getAnalytics().setCallIsInterrupted(true); getForegroundCall().getAnalytics().setCallIsInterrupted(true); call.getAnalytics().setCallIsAdditional(true); call.getAnalytics().setCallIsAdditional(true); } } setIntentExtrasAndStartTime(call, extras); setIntentExtrasAndStartTime(call, extras); // TODO: Move this to be a part of addCall() // TODO: Move this to be a part of addCall() call.addListener(this); call.addListener(this); Loading Loading @@ -912,8 +911,10 @@ public class CallsManager extends Call.ListenerBase if (isHandoverAllowed) { if (isHandoverAllowed) { // Link the calls so we know we're handing over. // Link the calls so we know we're handing over. fromCall.setHandoverToCall(call); fromCall.setHandoverDestinationCall(call); call.setHandoverFromCall(fromCall); call.setHandoverSourceCall(fromCall); call.setHandoverState(HandoverState.HANDOVER_TO_STARTED); fromCall.setHandoverState(HandoverState.HANDOVER_FROM_STARTED); Log.addEvent(fromCall, LogUtils.Events.START_HANDOVER, Log.addEvent(fromCall, LogUtils.Events.START_HANDOVER, "handOverFrom=%s, handOverTo=%s", fromCall.getId(), call.getId()); "handOverFrom=%s, handOverTo=%s", fromCall.getId(), call.getId()); Log.addEvent(call, LogUtils.Events.START_HANDOVER, Log.addEvent(call, LogUtils.Events.START_HANDOVER, Loading @@ -921,9 +922,9 @@ public class CallsManager extends Call.ListenerBase } } } else { } else { Log.w(this, "processIncomingCallIntent: To account doesn't support handover."); Log.w(this, "processIncomingCallIntent: To account doesn't support handover."); isHandoverAllowed = false; } } } } if (!isHandoverAllowed || (call.isSelfManaged() && !isIncomingCallPermitted(call, if (!isHandoverAllowed || (call.isSelfManaged() && !isIncomingCallPermitted(call, call.getTargetPhoneAccount()))) { call.getTargetPhoneAccount()))) { notifyCreateConnectionFailed(phoneAccountHandle, call); notifyCreateConnectionFailed(phoneAccountHandle, call); Loading Loading @@ -1721,8 +1722,7 @@ public class CallsManager extends Call.ListenerBase * Removes an existing disconnected call, and notifies the in-call app. * Removes an existing disconnected call, and notifies the in-call app. */ */ void markCallAsRemoved(Call call) { void markCallAsRemoved(Call call) { call.markHandoverFailed(); call.maybeCleanupHandover(); removeCall(call); removeCall(call); Call foregroundCall = mCallAudioManager.getPossiblyHeldForegroundCall(); Call foregroundCall = mCallAudioManager.getPossiblyHeldForegroundCall(); if (mLocallyDisconnectingCalls.contains(call)) { if (mLocallyDisconnectingCalls.contains(call)) { Loading Loading @@ -2177,46 +2177,71 @@ public class CallsManager extends Call.ListenerBase Trace.beginSection("onCallStateChanged"); Trace.beginSection("onCallStateChanged"); // If this call became active because it is being handed over from another Call, the maybeHandleHandover(call, newState); // call which was being handed over from can be disconnected at this point. if (call.getHandoverFromCall() != null) { // Only broadcast state change for calls that are being tracked. if (mCalls.contains(call)) { updateCanAddCall(); for (CallsManagerListener listener : mListeners) { if (LogUtils.SYSTRACE_DEBUG) { Trace.beginSection(listener.getClass().toString() + " onCallStateChanged"); } listener.onCallStateChanged(call, oldState, newState); if (LogUtils.SYSTRACE_DEBUG) { Trace.endSection(); } } } Trace.endSection(); } } /** * 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) { if (newState == CallState.ACTIVE) { Call handoverFrom = call.getHandoverFromCall(); // This call went active, so the user has accepted the handover. Log.addEvent(call, LogUtils.Events.HANDOVER_COMPLETE, "from=%s, to=%s", Log.i(this, "setCallState: handover to accepted"); handoverFrom.getId(), call.getId()); acceptHandoverTo(call); 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) { } else if (newState == CallState.DISCONNECTED) { Call handoverFrom = call.getHandoverFromCall(); // The call was disconnected, so the user has rejected the handover. Log.i(this, "Call %s failed to handover from %s.", Log.i(this, "setCallState: handover to rejected"); call.getId(), handoverFrom.getId()); rejectHandoverTo(call); 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 // If this call was disconnected because it was handed over TO another call, report the // handover as complete. // handover as complete. } else if (call.getHandoverToCall() != null && newState == CallState.DISCONNECTED) { } else if (call.getHandoverDestinationCall() != null Call handoverTo = call.getHandoverToCall(); && 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", Log.addEvent(handoverTo, LogUtils.Events.HANDOVER_COMPLETE, "from=%s, to=%s", call.getId(), handoverTo.getId()); call.getId(), handoverTo.getId()); Log.addEvent(call, LogUtils.Events.HANDOVER_COMPLETE, "from=%s, to=%s", Log.addEvent(call, LogUtils.Events.HANDOVER_COMPLETE, "from=%s, to=%s", Loading @@ -2229,24 +2254,45 @@ public class CallsManager extends Call.ListenerBase // Inform the "to" ConnectionService that handover to it has completed. // Inform the "to" ConnectionService that handover to it has completed. handoverTo.sendCallEvent(android.telecom.Call.EVENT_HANDOVER_COMPLETE, null); handoverTo.sendCallEvent(android.telecom.Call.EVENT_HANDOVER_COMPLETE, null); answerCall(handoverTo, handoverTo.getVideoState()); answerCall(handoverTo, handoverTo.getVideoState()); call.markHandoverSuccess(); call.markFinishedHandoverStateAndCleanup(HandoverState.HANDOVER_COMPLETE); } } // Only broadcast state change for calls that are being tracked. private void rejectHandoverTo(Call handoverTo) { if (mCalls.contains(call)) { Call handoverFrom = handoverTo.getHandoverSourceCall(); updateCanAddCall(); Log.i(this, "rejectHandoverTo: from=%s, to=%s", handoverFrom.getId(), handoverTo.getId()); for (CallsManagerListener listener : mListeners) { Log.addEvent(handoverFrom, LogUtils.Events.HANDOVER_FAILED, "from=%s, to=%s", if (LogUtils.SYSTRACE_DEBUG) { handoverTo.getId(), handoverFrom.getId()); Trace.beginSection(listener.getClass().toString() + " onCallStateChanged"); Log.addEvent(handoverTo, LogUtils.Events.HANDOVER_FAILED, "from=%s, to=%s", } handoverTo.getId(), handoverFrom.getId()); listener.onCallStateChanged(call, oldState, newState); if (LogUtils.SYSTRACE_DEBUG) { // Inform the "from" Call (ie the source call) that the handover from it has Trace.endSection(); // 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); } } Trace.endSection(); 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() { private void updateCanAddCall() { Loading Loading @@ -2455,7 +2501,7 @@ public class CallsManager extends Call.ListenerBase public boolean shouldShowSystemIncomingCallUi(Call incomingCall) { public boolean shouldShowSystemIncomingCallUi(Call incomingCall) { return incomingCall.isIncoming() && incomingCall.isSelfManaged() && return incomingCall.isIncoming() && incomingCall.isSelfManaged() && hasCallsForOtherPhoneAccount(incomingCall.getTargetPhoneAccount()) && hasCallsForOtherPhoneAccount(incomingCall.getTargetPhoneAccount()) && incomingCall.getHandoverFromCall() == null; incomingCall.getHandoverSourceCall() == null; } } private boolean makeRoomForOutgoingCall(Call call, boolean isEmergency) { private boolean makeRoomForOutgoingCall(Call call, boolean isEmergency) { Loading Loading @@ -2785,7 +2831,7 @@ public class CallsManager extends Call.ListenerBase // Only permit outgoing calls if there is no ongoing emergency calls and all other calls // Only permit outgoing calls if there is no ongoing emergency calls and all other calls // are associated with the current PhoneAccountHandle. // are associated with the current PhoneAccountHandle. return !hasEmergencyCall() && ( return !hasEmergencyCall() && ( excludeCall.getHandoverFromCall() != null || excludeCall.getHandoverSourceCall() != null || (!hasMaximumSelfManagedCalls(excludeCall, phoneAccountHandle) && (!hasMaximumSelfManagedCalls(excludeCall, phoneAccountHandle) && !hasCallsForOtherPhoneAccount(phoneAccountHandle) && !hasCallsForOtherPhoneAccount(phoneAccountHandle) && !hasManagedCalls())); !hasManagedCalls())); Loading Loading @@ -3046,6 +3092,8 @@ public class CallsManager extends Call.ListenerBase Bundle extras = new Bundle(); Bundle extras = new Bundle(); extras.putBoolean(TelecomManager.EXTRA_IS_HANDOVER, true); 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); extras.putInt(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, videoState); if (initiatingExtras != null) { if (initiatingExtras != null) { extras.putAll(initiatingExtras); extras.putAll(initiatingExtras); Loading @@ -3056,8 +3104,10 @@ public class CallsManager extends Call.ListenerBase extras, getCurrentUserHandle(), null /* originalIntent */); extras, getCurrentUserHandle(), null /* originalIntent */); Log.addEvent(handoverFromCall, LogUtils.Events.START_HANDOVER, Log.addEvent(handoverFromCall, LogUtils.Events.START_HANDOVER, "handOverFrom=%s, handOverTo=%s", handoverFromCall.getId(), handoverToCall.getId()); "handOverFrom=%s, handOverTo=%s", handoverFromCall.getId(), handoverToCall.getId()); handoverFromCall.setHandoverToCall(handoverToCall); handoverFromCall.setHandoverDestinationCall(handoverToCall); handoverToCall.setHandoverFromCall(handoverFromCall); handoverFromCall.setHandoverState(HandoverState.HANDOVER_FROM_STARTED); handoverToCall.setHandoverState(HandoverState.HANDOVER_TO_STARTED); handoverToCall.setHandoverSourceCall(handoverFromCall); handoverToCall.setNewOutgoingCallIntentBroadcastIsDone(); handoverToCall.setNewOutgoingCallIntentBroadcastIsDone(); placeOutgoingCall(handoverToCall, handoverToCall.getHandle(), null /* gatewayInfo */, placeOutgoingCall(handoverToCall, handoverToCall.getHandle(), null /* gatewayInfo */, false /* startwithSpeaker */, false /* startwithSpeaker */, Loading Loading @@ -3109,8 +3159,8 @@ public class CallsManager extends Call.ListenerBase * @return {@code true} if a call in the process of handover exists, {@code false} otherwise. * @return {@code true} if a call in the process of handover exists, {@code false} otherwise. */ */ private boolean isHandoverInProgress() { private boolean isHandoverInProgress() { return mCalls.stream().filter(c -> c.getHandoverFromCall() != null || return mCalls.stream().filter(c -> c.getHandoverSourceCall() != null || c.getHandoverToCall() != null).count() > 0; c.getHandoverDestinationCall() != null).count() > 0; } } private void broadcastUnregisterIntent(PhoneAccountHandle accountHandle) { private void broadcastUnregisterIntent(PhoneAccountHandle accountHandle) { Loading src/com/android/server/telecom/ConnectionServiceWrapper.java +3 −1 Original line number Original line Diff line number Diff line Loading @@ -930,8 +930,10 @@ public class ConnectionServiceWrapper extends ServiceBinder { // Call is incoming and added because we're handing over from another; tell CS // Call is incoming and added because we're handing over from another; tell CS // that its expected to handover. // 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.putBoolean(TelecomManager.EXTRA_IS_HANDOVER, true); extras.putParcelable(TelecomManager.EXTRA_HANDOVER_FROM_PHONE_ACCOUNT, call.getHandoverSourceCall().getTargetPhoneAccount()); } } Log.addEvent(call, LogUtils.Events.START_CONNECTION, Log.addEvent(call, LogUtils.Events.START_CONNECTION, Loading src/com/android/server/telecom/HandoverState.java 0 → 100644 +58 −0 Original line number Original line Diff line number Diff line /* * 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 ""; } } Loading
src/com/android/server/telecom/Call.java +38 −41 Original line number Original line Diff line number Diff line Loading @@ -456,20 +456,18 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable { * When a call handover has been initiated via {@link #requestHandover(PhoneAccountHandle, * When a call handover has been initiated via {@link #requestHandover(PhoneAccountHandle, * int, Bundle)}, contains the call which this call is being handed over to. * 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, * When a call handover has been initiated via {@link #requestHandover(PhoneAccountHandle, * int, Bundle)}, contains the call which this call is being handed over from. * 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, * Indicates the current state of this call if it is in the process of a handover. * 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. */ */ private boolean mIsHandoverSuccessful = false; private int mHandoverState = HandoverState.HANDOVER_NONE; /** /** * Persists the specified parameters and initializes the new instance. * Persists the specified parameters and initializes the new instance. Loading Loading @@ -1091,57 +1089,56 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable { setConnectionProperties(getConnectionProperties()); setConnectionProperties(getConnectionProperties()); } } /** public void markFinishedHandoverStateAndCleanup(int handoverState) { * Marks a handover as failed. if (mHandoverSourceCall != null) { */ mHandoverSourceCall.setHandoverState(handoverState); public void markHandoverFailed() { } else if (mHandoverDestinationCall != null) { markHandoverResult(false /* isComplete */); mHandoverDestinationCall.setHandoverState(handoverState); } } setHandoverState(handoverState); /** maybeCleanupHandover(); * Marks a handover as being successful. */ public void markHandoverSuccess() { markHandoverResult(true /* isComplete */); } } private void markHandoverResult(boolean isHandoverSuccessful) { public void maybeCleanupHandover() { if (mHandoverFromCall != null) { if (mHandoverSourceCall != null) { mHandoverFromCall.mIsHandoverSuccessful = isHandoverSuccessful; mHandoverSourceCall.setHandoverSourceCall(null); mHandoverFromCall.setHandoverFromCall(null); mHandoverSourceCall.setHandoverDestinationCall(null); mHandoverFromCall.setHandoverToCall(null); mHandoverSourceCall = null; mHandoverFromCall = null; } else if (mHandoverDestinationCall != null) { } else if (mHandoverToCall != null) { mHandoverDestinationCall.setHandoverSourceCall(null); mHandoverToCall.mIsHandoverSuccessful = isHandoverSuccessful; mHandoverDestinationCall.setHandoverDestinationCall(null); mHandoverToCall.setHandoverFromCall(null); mHandoverDestinationCall = null; mHandoverToCall.setHandoverToCall(null); mHandoverToCall = null; } } mIsHandoverSuccessful = isHandoverSuccessful; } } public boolean isHandoverInProgress() { public boolean isHandoverInProgress() { return mHandoverFromCall != null || mHandoverToCall != null; return mHandoverSourceCall != null || mHandoverDestinationCall != null; } public Call getHandoverDestinationCall() { return mHandoverDestinationCall; } } public Call getHandoverToCall() { public void setHandoverDestinationCall(Call call) { return mHandoverToCall; mHandoverDestinationCall = call; } } public void setHandoverToCall(Call call) { public Call getHandoverSourceCall() { mHandoverToCall = call; return mHandoverSourceCall; } } public Call getHandoverFromCall() { public void setHandoverSourceCall(Call call) { return mHandoverFromCall; mHandoverSourceCall = call; } } public void setHandoverFromCall(Call call) { public void setHandoverState(int handoverState) { mHandoverFromCall = call; Log.d(this, "setHandoverState: callId=%s, handoverState=%s", getId(), HandoverState.stateToString(handoverState)); mHandoverState = handoverState; } } public boolean isHandoverSuccessful() { public int getHandoverState() { return mIsHandoverSuccessful; return mHandoverState; } } private void configureIsWorkCall() { private void configureIsWorkCall() { Loading
src/com/android/server/telecom/CallLogManager.java +2 −1 Original line number Original line Diff line number Diff line Loading @@ -153,7 +153,8 @@ public final class CallLogManager extends CallsManagerListenerBase { !isCallCanceled) && !isCallCanceled) && !call.isExternalCall() && !call.isExternalCall() && (!call.isSelfManaged() || (!call.isSelfManaged() || call.isLoggedSelfManaged())) { call.isLoggedSelfManaged() && call.getHandoverState() != HandoverState.HANDOVER_FAILED)) { int type; int type; if (!call.isIncoming()) { if (!call.isIncoming()) { type = Calls.OUTGOING_TYPE; type = Calls.OUTGOING_TYPE; Loading
src/com/android/server/telecom/CallsManager.java +117 −67 Original line number Original line Diff line number Diff line Loading @@ -884,7 +884,6 @@ public class CallsManager extends Call.ListenerBase getForegroundCall().getAnalytics().setCallIsInterrupted(true); getForegroundCall().getAnalytics().setCallIsInterrupted(true); call.getAnalytics().setCallIsAdditional(true); call.getAnalytics().setCallIsAdditional(true); } } setIntentExtrasAndStartTime(call, extras); setIntentExtrasAndStartTime(call, extras); // TODO: Move this to be a part of addCall() // TODO: Move this to be a part of addCall() call.addListener(this); call.addListener(this); Loading Loading @@ -912,8 +911,10 @@ public class CallsManager extends Call.ListenerBase if (isHandoverAllowed) { if (isHandoverAllowed) { // Link the calls so we know we're handing over. // Link the calls so we know we're handing over. fromCall.setHandoverToCall(call); fromCall.setHandoverDestinationCall(call); call.setHandoverFromCall(fromCall); call.setHandoverSourceCall(fromCall); call.setHandoverState(HandoverState.HANDOVER_TO_STARTED); fromCall.setHandoverState(HandoverState.HANDOVER_FROM_STARTED); Log.addEvent(fromCall, LogUtils.Events.START_HANDOVER, Log.addEvent(fromCall, LogUtils.Events.START_HANDOVER, "handOverFrom=%s, handOverTo=%s", fromCall.getId(), call.getId()); "handOverFrom=%s, handOverTo=%s", fromCall.getId(), call.getId()); Log.addEvent(call, LogUtils.Events.START_HANDOVER, Log.addEvent(call, LogUtils.Events.START_HANDOVER, Loading @@ -921,9 +922,9 @@ public class CallsManager extends Call.ListenerBase } } } else { } else { Log.w(this, "processIncomingCallIntent: To account doesn't support handover."); Log.w(this, "processIncomingCallIntent: To account doesn't support handover."); isHandoverAllowed = false; } } } } if (!isHandoverAllowed || (call.isSelfManaged() && !isIncomingCallPermitted(call, if (!isHandoverAllowed || (call.isSelfManaged() && !isIncomingCallPermitted(call, call.getTargetPhoneAccount()))) { call.getTargetPhoneAccount()))) { notifyCreateConnectionFailed(phoneAccountHandle, call); notifyCreateConnectionFailed(phoneAccountHandle, call); Loading Loading @@ -1721,8 +1722,7 @@ public class CallsManager extends Call.ListenerBase * Removes an existing disconnected call, and notifies the in-call app. * Removes an existing disconnected call, and notifies the in-call app. */ */ void markCallAsRemoved(Call call) { void markCallAsRemoved(Call call) { call.markHandoverFailed(); call.maybeCleanupHandover(); removeCall(call); removeCall(call); Call foregroundCall = mCallAudioManager.getPossiblyHeldForegroundCall(); Call foregroundCall = mCallAudioManager.getPossiblyHeldForegroundCall(); if (mLocallyDisconnectingCalls.contains(call)) { if (mLocallyDisconnectingCalls.contains(call)) { Loading Loading @@ -2177,46 +2177,71 @@ public class CallsManager extends Call.ListenerBase Trace.beginSection("onCallStateChanged"); Trace.beginSection("onCallStateChanged"); // If this call became active because it is being handed over from another Call, the maybeHandleHandover(call, newState); // call which was being handed over from can be disconnected at this point. if (call.getHandoverFromCall() != null) { // Only broadcast state change for calls that are being tracked. if (mCalls.contains(call)) { updateCanAddCall(); for (CallsManagerListener listener : mListeners) { if (LogUtils.SYSTRACE_DEBUG) { Trace.beginSection(listener.getClass().toString() + " onCallStateChanged"); } listener.onCallStateChanged(call, oldState, newState); if (LogUtils.SYSTRACE_DEBUG) { Trace.endSection(); } } } Trace.endSection(); } } /** * 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) { if (newState == CallState.ACTIVE) { Call handoverFrom = call.getHandoverFromCall(); // This call went active, so the user has accepted the handover. Log.addEvent(call, LogUtils.Events.HANDOVER_COMPLETE, "from=%s, to=%s", Log.i(this, "setCallState: handover to accepted"); handoverFrom.getId(), call.getId()); acceptHandoverTo(call); 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) { } else if (newState == CallState.DISCONNECTED) { Call handoverFrom = call.getHandoverFromCall(); // The call was disconnected, so the user has rejected the handover. Log.i(this, "Call %s failed to handover from %s.", Log.i(this, "setCallState: handover to rejected"); call.getId(), handoverFrom.getId()); rejectHandoverTo(call); 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 // If this call was disconnected because it was handed over TO another call, report the // handover as complete. // handover as complete. } else if (call.getHandoverToCall() != null && newState == CallState.DISCONNECTED) { } else if (call.getHandoverDestinationCall() != null Call handoverTo = call.getHandoverToCall(); && 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", Log.addEvent(handoverTo, LogUtils.Events.HANDOVER_COMPLETE, "from=%s, to=%s", call.getId(), handoverTo.getId()); call.getId(), handoverTo.getId()); Log.addEvent(call, LogUtils.Events.HANDOVER_COMPLETE, "from=%s, to=%s", Log.addEvent(call, LogUtils.Events.HANDOVER_COMPLETE, "from=%s, to=%s", Loading @@ -2229,24 +2254,45 @@ public class CallsManager extends Call.ListenerBase // Inform the "to" ConnectionService that handover to it has completed. // Inform the "to" ConnectionService that handover to it has completed. handoverTo.sendCallEvent(android.telecom.Call.EVENT_HANDOVER_COMPLETE, null); handoverTo.sendCallEvent(android.telecom.Call.EVENT_HANDOVER_COMPLETE, null); answerCall(handoverTo, handoverTo.getVideoState()); answerCall(handoverTo, handoverTo.getVideoState()); call.markHandoverSuccess(); call.markFinishedHandoverStateAndCleanup(HandoverState.HANDOVER_COMPLETE); } } // Only broadcast state change for calls that are being tracked. private void rejectHandoverTo(Call handoverTo) { if (mCalls.contains(call)) { Call handoverFrom = handoverTo.getHandoverSourceCall(); updateCanAddCall(); Log.i(this, "rejectHandoverTo: from=%s, to=%s", handoverFrom.getId(), handoverTo.getId()); for (CallsManagerListener listener : mListeners) { Log.addEvent(handoverFrom, LogUtils.Events.HANDOVER_FAILED, "from=%s, to=%s", if (LogUtils.SYSTRACE_DEBUG) { handoverTo.getId(), handoverFrom.getId()); Trace.beginSection(listener.getClass().toString() + " onCallStateChanged"); Log.addEvent(handoverTo, LogUtils.Events.HANDOVER_FAILED, "from=%s, to=%s", } handoverTo.getId(), handoverFrom.getId()); listener.onCallStateChanged(call, oldState, newState); if (LogUtils.SYSTRACE_DEBUG) { // Inform the "from" Call (ie the source call) that the handover from it has Trace.endSection(); // 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); } } Trace.endSection(); 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() { private void updateCanAddCall() { Loading Loading @@ -2455,7 +2501,7 @@ public class CallsManager extends Call.ListenerBase public boolean shouldShowSystemIncomingCallUi(Call incomingCall) { public boolean shouldShowSystemIncomingCallUi(Call incomingCall) { return incomingCall.isIncoming() && incomingCall.isSelfManaged() && return incomingCall.isIncoming() && incomingCall.isSelfManaged() && hasCallsForOtherPhoneAccount(incomingCall.getTargetPhoneAccount()) && hasCallsForOtherPhoneAccount(incomingCall.getTargetPhoneAccount()) && incomingCall.getHandoverFromCall() == null; incomingCall.getHandoverSourceCall() == null; } } private boolean makeRoomForOutgoingCall(Call call, boolean isEmergency) { private boolean makeRoomForOutgoingCall(Call call, boolean isEmergency) { Loading Loading @@ -2785,7 +2831,7 @@ public class CallsManager extends Call.ListenerBase // Only permit outgoing calls if there is no ongoing emergency calls and all other calls // Only permit outgoing calls if there is no ongoing emergency calls and all other calls // are associated with the current PhoneAccountHandle. // are associated with the current PhoneAccountHandle. return !hasEmergencyCall() && ( return !hasEmergencyCall() && ( excludeCall.getHandoverFromCall() != null || excludeCall.getHandoverSourceCall() != null || (!hasMaximumSelfManagedCalls(excludeCall, phoneAccountHandle) && (!hasMaximumSelfManagedCalls(excludeCall, phoneAccountHandle) && !hasCallsForOtherPhoneAccount(phoneAccountHandle) && !hasCallsForOtherPhoneAccount(phoneAccountHandle) && !hasManagedCalls())); !hasManagedCalls())); Loading Loading @@ -3046,6 +3092,8 @@ public class CallsManager extends Call.ListenerBase Bundle extras = new Bundle(); Bundle extras = new Bundle(); extras.putBoolean(TelecomManager.EXTRA_IS_HANDOVER, true); 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); extras.putInt(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, videoState); if (initiatingExtras != null) { if (initiatingExtras != null) { extras.putAll(initiatingExtras); extras.putAll(initiatingExtras); Loading @@ -3056,8 +3104,10 @@ public class CallsManager extends Call.ListenerBase extras, getCurrentUserHandle(), null /* originalIntent */); extras, getCurrentUserHandle(), null /* originalIntent */); Log.addEvent(handoverFromCall, LogUtils.Events.START_HANDOVER, Log.addEvent(handoverFromCall, LogUtils.Events.START_HANDOVER, "handOverFrom=%s, handOverTo=%s", handoverFromCall.getId(), handoverToCall.getId()); "handOverFrom=%s, handOverTo=%s", handoverFromCall.getId(), handoverToCall.getId()); handoverFromCall.setHandoverToCall(handoverToCall); handoverFromCall.setHandoverDestinationCall(handoverToCall); handoverToCall.setHandoverFromCall(handoverFromCall); handoverFromCall.setHandoverState(HandoverState.HANDOVER_FROM_STARTED); handoverToCall.setHandoverState(HandoverState.HANDOVER_TO_STARTED); handoverToCall.setHandoverSourceCall(handoverFromCall); handoverToCall.setNewOutgoingCallIntentBroadcastIsDone(); handoverToCall.setNewOutgoingCallIntentBroadcastIsDone(); placeOutgoingCall(handoverToCall, handoverToCall.getHandle(), null /* gatewayInfo */, placeOutgoingCall(handoverToCall, handoverToCall.getHandle(), null /* gatewayInfo */, false /* startwithSpeaker */, false /* startwithSpeaker */, Loading Loading @@ -3109,8 +3159,8 @@ public class CallsManager extends Call.ListenerBase * @return {@code true} if a call in the process of handover exists, {@code false} otherwise. * @return {@code true} if a call in the process of handover exists, {@code false} otherwise. */ */ private boolean isHandoverInProgress() { private boolean isHandoverInProgress() { return mCalls.stream().filter(c -> c.getHandoverFromCall() != null || return mCalls.stream().filter(c -> c.getHandoverSourceCall() != null || c.getHandoverToCall() != null).count() > 0; c.getHandoverDestinationCall() != null).count() > 0; } } private void broadcastUnregisterIntent(PhoneAccountHandle accountHandle) { private void broadcastUnregisterIntent(PhoneAccountHandle accountHandle) { Loading
src/com/android/server/telecom/ConnectionServiceWrapper.java +3 −1 Original line number Original line Diff line number Diff line Loading @@ -930,8 +930,10 @@ public class ConnectionServiceWrapper extends ServiceBinder { // Call is incoming and added because we're handing over from another; tell CS // Call is incoming and added because we're handing over from another; tell CS // that its expected to handover. // 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.putBoolean(TelecomManager.EXTRA_IS_HANDOVER, true); extras.putParcelable(TelecomManager.EXTRA_HANDOVER_FROM_PHONE_ACCOUNT, call.getHandoverSourceCall().getTargetPhoneAccount()); } } Log.addEvent(call, LogUtils.Events.START_CONNECTION, Log.addEvent(call, LogUtils.Events.START_CONNECTION, Loading
src/com/android/server/telecom/HandoverState.java 0 → 100644 +58 −0 Original line number Original line Diff line number Diff line /* * 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 ""; } }