Merge "Add notif. and foreground service to transactional test app" into udc-dev
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index e195fd2..d775350 100644
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -6090,34 +6090,53 @@
}
/**
- * Intended for ongoing or new calls that would like to go active/answered and need to
- * update the mConnectionSvrFocusMgr before setting the state
+ * This helper mainly requests mConnectionSvrFocusMgr to update the call focus via a
+ * {@link TransactionalFocusRequestCallback}. However, in the case of a held call, the
+ * state must be set first and then a request must be made.
+ *
+ * @param newCallFocus to set active/answered
+ * @param resultCallback that back propagates the focusManager result
+ *
+ * Note: This method should only be called if there are no active calls.
*/
- public void transactionRequestNewFocusCall(Call call, int newCallState,
- OutcomeReceiver<Boolean, CallException> callback) {
- Log.d(this, "transactionRequestNewFocusCall");
- PendingAction pendingAction = new ActionSetCallState(call, newCallState,
- "transactional ActionSetCallState");
+ public void requestNewCallFocusAndVerify(Call newCallFocus,
+ OutcomeReceiver<Boolean, CallException> resultCallback) {
+ int currentCallState = newCallFocus.getState();
+ PendingAction pendingAction = null;
+
+ // if the current call is in a state that can become the new call focus, we can set the
+ // state afterwards...
+ if (ConnectionServiceFocusManager.PRIORITY_FOCUS_CALL_STATE.contains(currentCallState)) {
+ pendingAction = new ActionSetCallState(newCallFocus, CallState.ACTIVE,
+ "vCFC: pending action set state");
+ } else {
+ // However, HELD calls need to be set to ACTIVE before requesting call focus.
+ setCallState(newCallFocus, CallState.ACTIVE, "vCFC: immediately set active");
+ }
+
mConnectionSvrFocusMgr
- .requestFocus(call,
- new TransactionalFocusRequestCallback(pendingAction, call, callback));
+ .requestFocus(newCallFocus,
+ new TransactionalFocusRequestCallback(pendingAction, currentCallState,
+ newCallFocus, resultCallback));
}
/**
* Request a new call focus and ensure the request was successful via an OutcomeReceiver. Also,
- * include a PendingAction that will execute if the call focus change is successful.
+ * conditionally include a PendingAction that will execute if and only if the call focus change
+ * is successful.
*/
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public class TransactionalFocusRequestCallback implements
ConnectionServiceFocusManager.RequestFocusCallback {
private PendingAction mPendingAction;
- @NonNull
- private Call mTargetCallFocus;
+ private int mPreviousCallState;
+ @NonNull private Call mTargetCallFocus;
private OutcomeReceiver<Boolean, CallException> mCallback;
- TransactionalFocusRequestCallback(PendingAction pendingAction, @NonNull Call call,
- OutcomeReceiver<Boolean, CallException> callback) {
+ TransactionalFocusRequestCallback(PendingAction pendingAction, int previousState,
+ @NonNull Call call, OutcomeReceiver<Boolean, CallException> callback) {
mPendingAction = pendingAction;
+ mPreviousCallState = previousState;
mTargetCallFocus = call;
mCallback = callback;
}
@@ -6130,12 +6149,18 @@
mTargetCallFocus, currentCallFocus);
if (currentCallFocus == null ||
!currentCallFocus.getId().equals(mTargetCallFocus.getId())) {
+ // possibly reset the call state
+ if (mTargetCallFocus.getState() != mPreviousCallState) {
+ mTargetCallFocus.setState(mPreviousCallState, "resetting call state");
+ }
mCallback.onError(new CallException("failed to switch focus to requested call",
CallException.CODE_CALL_CANNOT_BE_SET_TO_ACTIVE));
return;
}
// at this point, we know the FocusManager is able to update successfully
- mPendingAction.performAction(); // set the call state
+ if (mPendingAction != null) {
+ mPendingAction.performAction(); // set the call state
+ }
mCallback.onResult(true); // complete the transaction
}
}
diff --git a/src/com/android/server/telecom/ConnectionServiceFocusManager.java b/src/com/android/server/telecom/ConnectionServiceFocusManager.java
index 8db98e9..6fbc494 100644
--- a/src/com/android/server/telecom/ConnectionServiceFocusManager.java
+++ b/src/com/android/server/telecom/ConnectionServiceFocusManager.java
@@ -32,6 +32,7 @@
import java.util.List;
import java.util.Objects;
import java.util.Optional;
+import java.util.Set;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
@@ -153,10 +154,9 @@
void setCallsManagerListener(CallsManager.CallsManagerListener listener);
}
- private static final int[] PRIORITY_FOCUS_CALL_STATE = new int[] {
- CallState.ACTIVE, CallState.CONNECTING, CallState.DIALING, CallState.AUDIO_PROCESSING,
- CallState.RINGING
- };
+ public static final Set<Integer> PRIORITY_FOCUS_CALL_STATE
+ = Set.of(CallState.ACTIVE, CallState.CONNECTING, CallState.DIALING,
+ CallState.AUDIO_PROCESSING, CallState.RINGING);
private static final int MSG_REQUEST_FOCUS = 1;
private static final int MSG_RELEASE_CONNECTION_FOCUS = 2;
@@ -374,17 +374,15 @@
&& call.isFocusable())
.collect(Collectors.toList());
- for (int i = 0; i < PRIORITY_FOCUS_CALL_STATE.length; i++) {
- for (CallFocus call : calls) {
- if (call.getState() == PRIORITY_FOCUS_CALL_STATE[i]) {
- mCurrentFocusCall = call;
- Log.d(this, "updateCurrentFocusCall %s", mCurrentFocusCall);
- return;
- }
+ for (CallFocus call : calls) {
+ if (PRIORITY_FOCUS_CALL_STATE.contains(call.getState())) {
+ mCurrentFocusCall = call;
+ Log.i(this, "updateCurrentFocusCall %s", mCurrentFocusCall);
+ return;
}
}
- Log.d(this, "updateCurrentFocusCall = null");
+ Log.i(this, "updateCurrentFocusCall = null");
}
private void onRequestFocusDone(FocusRequest focusRequest) {
diff --git a/src/com/android/server/telecom/TransactionalServiceWrapper.java b/src/com/android/server/telecom/TransactionalServiceWrapper.java
index 90acba8..1e6403e 100644
--- a/src/com/android/server/telecom/TransactionalServiceWrapper.java
+++ b/src/com/android/server/telecom/TransactionalServiceWrapper.java
@@ -38,14 +38,13 @@
import com.android.internal.telecom.ICallControl;
import com.android.internal.telecom.ICallEventCallback;
-import com.android.server.telecom.voip.AnswerCallTransaction;
import com.android.server.telecom.voip.CallEventCallbackAckTransaction;
import com.android.server.telecom.voip.EndpointChangeTransaction;
import com.android.server.telecom.voip.HoldCallTransaction;
import com.android.server.telecom.voip.EndCallTransaction;
-import com.android.server.telecom.voip.HoldActiveCallForNewCallTransaction;
+import com.android.server.telecom.voip.MaybeHoldCallForNewCallTransaction;
import com.android.server.telecom.voip.ParallelTransaction;
-import com.android.server.telecom.voip.RequestFocusTransaction;
+import com.android.server.telecom.voip.RequestNewActiveCallTransaction;
import com.android.server.telecom.voip.SerialTransaction;
import com.android.server.telecom.voip.TransactionManager;
import com.android.server.telecom.voip.VoipCallTransaction;
@@ -247,11 +246,12 @@
if (call != null) {
switch (action) {
case SET_ACTIVE:
- addTransactionsToManager(createSetActiveTransactions(call), callback);
+ handleCallControlNewCallFocusTransactions(call, SET_ACTIVE,
+ false /* isAnswer */, 0/*VideoState (ignored)*/, callback);
break;
case ANSWER:
- addTransactionsToManager(createSetAnswerTransactions(call,
- (int) objects[0]), callback);
+ handleCallControlNewCallFocusTransactions(call, ANSWER,
+ true /* isAnswer */, (int) objects[0] /*VideoState*/, callback);
break;
case DISCONNECT:
addTransactionsToManager(new EndCallTransaction(mCallsManager,
@@ -278,6 +278,32 @@
}
}
+ // The client is request their VoIP call state go ACTIVE/ANSWERED.
+ // This request is originating from the VoIP application.
+ private void handleCallControlNewCallFocusTransactions(Call call, String action,
+ boolean isAnswer, int potentiallyNewVideoState, ResultReceiver callback) {
+ mTransactionManager.addTransaction(createSetActiveTransactions(call),
+ new OutcomeReceiver<>() {
+ @Override
+ public void onResult(VoipCallTransactionResult result) {
+ Log.i(TAG, String.format(Locale.US,
+ "%s: onResult: callId=[%s]", action, call.getId()));
+ if (isAnswer) {
+ call.setVideoState(potentiallyNewVideoState);
+ }
+ callback.send(TELECOM_TRANSACTION_SUCCESS, new Bundle());
+ }
+
+ @Override
+ public void onError(CallException exception) {
+ Bundle extras = new Bundle();
+ extras.putParcelable(TRANSACTION_EXCEPTION_KEY, exception);
+ callback.send(exception == null ? CallException.CODE_ERROR_UNKNOWN :
+ exception.getCode(), extras);
+ }
+ });
+ }
+
@Override
public void requestCallEndpointChange(CallEndpoint endpoint, ResultReceiver callback) {
try {
@@ -299,14 +325,12 @@
Call call = mTrackedCalls.get(callId);
if (call != null) {
call.onConnectionEvent(event, extras);
- }
- else{
+ } else {
Log.i(TAG,
"sendEvent: was called but there is no call with id=[%s] cannot be "
+ "found. Most likely the call has been disconnected");
}
- }
- finally {
+ } finally {
Log.endSession();
}
}
@@ -348,7 +372,8 @@
try {
Log.startSession("TSW.oSA");
Log.d(TAG, String.format(Locale.US, "onSetActive: callId=[%s]", call.getId()));
- handleNewActiveCallCallbacks(call, ON_SET_ACTIVE, 0);
+ handleCallEventCallbackNewFocus(call, ON_SET_ACTIVE, false /*isAnswerRequest*/,
+ 0 /*VideoState*/);
} finally {
Log.endSession();
}
@@ -358,42 +383,51 @@
try {
Log.startSession("TSW.oA");
Log.d(TAG, String.format(Locale.US, "onAnswer: callId=[%s]", call.getId()));
- handleNewActiveCallCallbacks(call, ON_ANSWER, videoState);
+ handleCallEventCallbackNewFocus(call, ON_ANSWER, true /*isAnswerRequest*/,
+ videoState /*VideoState*/);
} finally {
Log.endSession();
}
}
- // need to create multiple transactions for onSetActive and onAnswer which both seek to set
- // the call to active
- private void handleNewActiveCallCallbacks(Call call, String action, int videoState) {
+ // handle a CallEventCallback to set a call ACTIVE/ANSWERED. Must get ack from client since the
+ // request has come from another source (ex. Android Auto is requesting a call to go active)
+ private void handleCallEventCallbackNewFocus(Call call, String action, boolean isAnswerRequest,
+ int potentiallyNewVideoState) {
// save CallsManager state before sending client state changes
Call foregroundCallBeforeSwap = mCallsManager.getForegroundCall();
boolean wasActive = foregroundCallBeforeSwap != null && foregroundCallBeforeSwap.isActive();
- // create 3 serial transactions:
- // -- hold active
- // -- set newCall as active
- // -- ack from client
SerialTransaction serialTransactions = createSetActiveTransactions(call);
- serialTransactions.appendTransaction(
- new CallEventCallbackAckTransaction(mICallEventCallback,
- action, call.getId(), videoState, mLock));
+ // 3. get ack from client (that the requested call can go active)
+ if (isAnswerRequest) {
+ serialTransactions.appendTransaction(
+ new CallEventCallbackAckTransaction(mICallEventCallback,
+ action, call.getId(), potentiallyNewVideoState, mLock));
+ } else {
+ serialTransactions.appendTransaction(
+ new CallEventCallbackAckTransaction(mICallEventCallback,
+ action, call.getId(), mLock));
+ }
// do CallsManager workload before asking client and
// reset CallsManager state if client does NOT ack
- mTransactionManager.addTransaction(serialTransactions, new OutcomeReceiver<>() {
- @Override
- public void onResult(VoipCallTransactionResult result) {
- Log.i(TAG, String.format(Locale.US,
- "%s: onResult: callId=[%s]", action, call.getId()));
- }
+ mTransactionManager.addTransaction(serialTransactions,
+ new OutcomeReceiver<>() {
+ @Override
+ public void onResult(VoipCallTransactionResult result) {
+ Log.i(TAG, String.format(Locale.US,
+ "%s: onResult: callId=[%s]", action, call.getId()));
+ if (isAnswerRequest) {
+ call.setVideoState(potentiallyNewVideoState);
+ }
+ }
- @Override
- public void onError(CallException exception) {
- maybeResetForegroundCall(foregroundCallBeforeSwap, wasActive);
- }
- });
+ @Override
+ public void onError(CallException exception) {
+ maybeResetForegroundCall(foregroundCallBeforeSwap, wasActive);
+ }
+ });
}
@@ -519,7 +553,7 @@
}
}
- public void onEvent(Call call, String event, Bundle extras){
+ public void onEvent(Call call, String event, Bundle extras) {
if (call != null) {
try {
mICallEventCallback.onEvent(call.getId(), event, extras);
@@ -553,27 +587,11 @@
// create list for multiple transactions
List<VoipCallTransaction> transactions = new ArrayList<>();
- // add t1. hold potential active call
- transactions.add(new HoldActiveCallForNewCallTransaction(mCallsManager, call));
+ // potentially hold the current active call in order to set a new call (active/answered)
+ transactions.add(new MaybeHoldCallForNewCallTransaction(mCallsManager, call));
+ // And request a new focus call update
+ transactions.add(new RequestNewActiveCallTransaction(mCallsManager, call));
- // add t2. send request to set the current call active
- transactions.add(new RequestFocusTransaction(mCallsManager, call));
-
- // send off to Transaction Manager to process
- return new SerialTransaction(transactions, mLock);
- }
-
- private SerialTransaction createSetAnswerTransactions(Call call, int videoState) {
- // create list for multiple transactions
- List<VoipCallTransaction> transactions = new ArrayList<>();
-
- // add t1. hold potential active call
- transactions.add(new HoldActiveCallForNewCallTransaction(mCallsManager, call));
-
- // add t2. answer current call
- transactions.add(new AnswerCallTransaction(mCallsManager, call, videoState));
-
- // send off to Transaction Manager to process
return new SerialTransaction(transactions, mLock);
}
diff --git a/src/com/android/server/telecom/voip/AnswerCallTransaction.java b/src/com/android/server/telecom/voip/AnswerCallTransaction.java
deleted file mode 100644
index efd2343..0000000
--- a/src/com/android/server/telecom/voip/AnswerCallTransaction.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (C) 2023 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.voip;
-
-import android.os.OutcomeReceiver;
-import android.telecom.CallException;
-import android.util.Log;
-
-import com.android.server.telecom.Call;
-import com.android.server.telecom.CallState;
-import com.android.server.telecom.CallsManager;
-
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.CompletionStage;
-
-/**
- * This transaction should be created for new incoming calls that request to go from
- * CallState.Ringing to CallState.Answered. Before changing the CallState, the focus manager must
- * be updated. Once the focus manager updates, the call state will be set. If there is an issue
- * answering the call, the transaction will fail.
- */
-public class AnswerCallTransaction extends VoipCallTransaction {
-
- private static final String TAG = AnswerCallTransaction.class.getSimpleName();
- private final CallsManager mCallsManager;
- private final Call mCall;
- private final int mVideoState;
-
- public AnswerCallTransaction(CallsManager callsManager, Call call, int videoState) {
- super(callsManager.getLock());
- mCallsManager = callsManager;
- mCall = call;
- mVideoState = videoState;
- }
-
- @Override
- public CompletionStage<VoipCallTransactionResult> processTransaction(Void v) {
- Log.d(TAG, "processTransaction");
- CompletableFuture<VoipCallTransactionResult> future = new CompletableFuture<>();
-
- mCall.setVideoState(mVideoState);
-
- mCallsManager.transactionRequestNewFocusCall(mCall, CallState.ANSWERED,
- new OutcomeReceiver<>() {
- @Override
- public void onResult(Boolean result) {
- Log.d(TAG, "processTransaction: onResult");
- future.complete(new VoipCallTransactionResult(
- VoipCallTransactionResult.RESULT_SUCCEED, null));
- }
-
- @Override
- public void onError(CallException exception) {
- Log.d(TAG, "processTransaction: onError");
- future.complete(new VoipCallTransactionResult(
- exception.getCode(), exception.getMessage()));
- }
- });
-
- return future;
- }
-}
\ No newline at end of file
diff --git a/src/com/android/server/telecom/voip/CallEventCallbackAckTransaction.java b/src/com/android/server/telecom/voip/CallEventCallbackAckTransaction.java
index 3d59ed3..8b4ffed 100644
--- a/src/com/android/server/telecom/voip/CallEventCallbackAckTransaction.java
+++ b/src/com/android/server/telecom/voip/CallEventCallbackAckTransaction.java
@@ -22,6 +22,7 @@
import android.os.Bundle;
import android.os.RemoteException;
import android.os.ResultReceiver;
+import android.telecom.CallAttributes;
import android.telecom.DisconnectCause;
import android.util.Log;
@@ -44,7 +45,7 @@
private final String mAction;
private final String mCallId;
// optional values
- private int mVideoState = 0;
+ private int mVideoState = CallAttributes.AUDIO_CALL;
private DisconnectCause mDisconnectCause = null;
private final VoipCallTransactionResult TRANSACTION_FAILED = new VoipCallTransactionResult(
diff --git a/src/com/android/server/telecom/voip/HoldActiveCallForNewCallTransaction.java b/src/com/android/server/telecom/voip/MaybeHoldCallForNewCallTransaction.java
similarity index 88%
rename from src/com/android/server/telecom/voip/HoldActiveCallForNewCallTransaction.java
rename to src/com/android/server/telecom/voip/MaybeHoldCallForNewCallTransaction.java
index ab203ad..a245c1c 100644
--- a/src/com/android/server/telecom/voip/HoldActiveCallForNewCallTransaction.java
+++ b/src/com/android/server/telecom/voip/MaybeHoldCallForNewCallTransaction.java
@@ -26,13 +26,13 @@
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
-public class HoldActiveCallForNewCallTransaction extends VoipCallTransaction {
+public class MaybeHoldCallForNewCallTransaction extends VoipCallTransaction {
- private static final String TAG = HoldActiveCallForNewCallTransaction.class.getSimpleName();
+ private static final String TAG = MaybeHoldCallForNewCallTransaction.class.getSimpleName();
private final CallsManager mCallsManager;
private final Call mCall;
- public HoldActiveCallForNewCallTransaction(CallsManager callsManager, Call call) {
+ public MaybeHoldCallForNewCallTransaction(CallsManager callsManager, Call call) {
super(callsManager.getLock());
mCallsManager = callsManager;
mCall = call;
diff --git a/src/com/android/server/telecom/voip/RequestFocusTransaction.java b/src/com/android/server/telecom/voip/RequestFocusTransaction.java
deleted file mode 100644
index cb4ee37..0000000
--- a/src/com/android/server/telecom/voip/RequestFocusTransaction.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2022 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.voip;
-
-import android.os.OutcomeReceiver;
-import android.telecom.CallException;
-import android.util.Log;
-
-import com.android.server.telecom.Call;
-import com.android.server.telecom.CallState;
-import com.android.server.telecom.CallsManager;
-
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.CompletionStage;
-
-public class RequestFocusTransaction extends VoipCallTransaction {
-
- private static final String TAG = RequestFocusTransaction.class.getSimpleName();
- private final CallsManager mCallsManager;
- private final Call mCall;
-
- public RequestFocusTransaction(CallsManager callsManager, Call call) {
- super(callsManager.getLock());
- mCallsManager = callsManager;
- mCall = call;
- }
-
- @Override
- public CompletionStage<VoipCallTransactionResult> processTransaction(Void v) {
- Log.d(TAG, "processTransaction");
- CompletableFuture<VoipCallTransactionResult> future = new CompletableFuture<>();
-
- mCallsManager.transactionRequestNewFocusCall(mCall, CallState.ACTIVE,
- new OutcomeReceiver<>() {
- @Override
- public void onResult(Boolean result) {
- Log.d(TAG, "processTransaction: onResult");
- future.complete(new VoipCallTransactionResult(
- VoipCallTransactionResult.RESULT_SUCCEED, null));
- }
-
- @Override
- public void onError(CallException exception) {
- Log.d(TAG, "processTransaction: onError");
- future.complete(new VoipCallTransactionResult(
- exception.getCode(), exception.getMessage()));
- }
- });
-
- return future;
- }
-}
\ No newline at end of file
diff --git a/src/com/android/server/telecom/voip/RequestNewActiveCallTransaction.java b/src/com/android/server/telecom/voip/RequestNewActiveCallTransaction.java
new file mode 100644
index 0000000..f586cc3
--- /dev/null
+++ b/src/com/android/server/telecom/voip/RequestNewActiveCallTransaction.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2022 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.voip;
+
+import android.os.OutcomeReceiver;
+import android.telecom.CallAttributes;
+import android.telecom.CallException;
+import android.util.Log;
+
+import com.android.server.telecom.Call;
+import com.android.server.telecom.CallState;
+import com.android.server.telecom.CallsManager;
+import com.android.server.telecom.ConnectionServiceFocusManager;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionStage;
+
+/**
+ * This transaction should be created when a requesting call would like to go from a valid inactive
+ * state (ex. HELD, RINGING, DIALING) to ACTIVE.
+ *
+ * This class performs some pre-checks to spot a failure in requesting a new call focus and sends
+ * the official request to transition the requested call to ACTIVE.
+ *
+ * Note:
+ * - This Transaction is used for CallControl and CallEventCallbacks, do not put logic in the
+ * onResult/onError that pertains to one direction.
+ * - MaybeHoldCallForNewCallTransaction was performed before this so any potential active calls
+ * should be held now.
+ */
+public class RequestNewActiveCallTransaction extends VoipCallTransaction {
+
+ private static final String TAG = RequestNewActiveCallTransaction.class.getSimpleName();
+ private final CallsManager mCallsManager;
+ private final Call mCall;
+
+ public RequestNewActiveCallTransaction(CallsManager callsManager, Call call) {
+ super(callsManager.getLock());
+ mCallsManager = callsManager;
+ mCall = call;
+ }
+
+ @Override
+ public CompletionStage<VoipCallTransactionResult> processTransaction(Void v) {
+ Log.d(TAG, "processTransaction");
+ CompletableFuture<VoipCallTransactionResult> future = new CompletableFuture<>();
+ int currentCallState = mCall.getState();
+
+ // certain calls cannot go active/answered (ex. disconnect calls, etc.)
+ if (!canBecomeNewCallFocus(currentCallState)) {
+ future.complete(new VoipCallTransactionResult(
+ CallException.CODE_CALL_CANNOT_BE_SET_TO_ACTIVE,
+ "CallState cannot be set to active or answered due to current call"
+ + " state being in invalid state"));
+ return future;
+ }
+
+ if (mCallsManager.getActiveCall() != null) {
+ future.complete(new VoipCallTransactionResult(
+ CallException.CODE_CALL_CANNOT_BE_SET_TO_ACTIVE,
+ "Already an active call. Request hold on current active call."));
+ return future;
+ }
+
+ mCallsManager.requestNewCallFocusAndVerify(mCall, new OutcomeReceiver<>() {
+ @Override
+ public void onResult(Boolean result) {
+ Log.d(TAG, "processTransaction: onResult");
+ future.complete(new VoipCallTransactionResult(
+ VoipCallTransactionResult.RESULT_SUCCEED, null));
+ }
+
+ @Override
+ public void onError(CallException exception) {
+ Log.d(TAG, "processTransaction: onError");
+ future.complete(new VoipCallTransactionResult(
+ exception.getCode(), exception.getMessage()));
+ }
+ });
+
+ return future;
+ }
+
+ private boolean isPriorityCallingState(int currentCallState) {
+ return ConnectionServiceFocusManager.PRIORITY_FOCUS_CALL_STATE.contains(currentCallState);
+ }
+
+ private boolean canBecomeNewCallFocus(int currentCallState) {
+ return isPriorityCallingState(currentCallState) || currentCallState == CallState.ON_HOLD;
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/server/telecom/voip/TransactionManager.java b/src/com/android/server/telecom/voip/TransactionManager.java
index a0955c8..98faf3d 100644
--- a/src/com/android/server/telecom/voip/TransactionManager.java
+++ b/src/com/android/server/telecom/voip/TransactionManager.java
@@ -16,6 +16,8 @@
package com.android.server.telecom.voip;
+import static android.telecom.CallException.CODE_OPERATION_TIMED_OUT;
+
import android.os.OutcomeReceiver;
import android.telecom.TelecomManager;
import android.telecom.CallException;
@@ -79,8 +81,8 @@
@Override
public void onTransactionTimeout(String transactionName){
- receiver.onResult(new VoipCallTransactionResult(
- VoipCallTransactionResult.RESULT_FAILED, transactionName + " timeout"));
+ receiver.onError(new CallException(transactionName + " timeout",
+ CODE_OPERATION_TIMED_OUT));
finishTransaction();
}
});
diff --git a/tests/src/com/android/server/telecom/tests/TransactionTests.java b/tests/src/com/android/server/telecom/tests/TransactionTests.java
index 1e6734b..3fc87a9 100644
--- a/tests/src/com/android/server/telecom/tests/TransactionTests.java
+++ b/tests/src/com/android/server/telecom/tests/TransactionTests.java
@@ -47,13 +47,12 @@
import com.android.server.telecom.PhoneNumberUtilsAdapter;
import com.android.server.telecom.TelecomSystem;
import com.android.server.telecom.ui.ToastFactory;
-import com.android.server.telecom.voip.AnswerCallTransaction;
import com.android.server.telecom.voip.EndCallTransaction;
import com.android.server.telecom.voip.HoldCallTransaction;
import com.android.server.telecom.voip.IncomingCallTransaction;
import com.android.server.telecom.voip.OutgoingCallTransaction;
-import com.android.server.telecom.voip.HoldActiveCallForNewCallTransaction;
-import com.android.server.telecom.voip.RequestFocusTransaction;
+import com.android.server.telecom.voip.MaybeHoldCallForNewCallTransaction;
+import com.android.server.telecom.voip.RequestNewActiveCallTransaction;
import org.junit.After;
import org.junit.Before;
@@ -143,40 +142,56 @@
}
@Test
- public void testTransactionalRequestFocus() throws Exception {
+ public void testRequestNewCallFocusWithDialingCall() throws Exception {
// GIVEN
- RequestFocusTransaction transaction =
- new RequestFocusTransaction(mCallsManager, mMockCall1);
+ RequestNewActiveCallTransaction transaction =
+ new RequestNewActiveCallTransaction(mCallsManager, mMockCall1);
// WHEN
+ when(mMockCall1.getState()).thenReturn(CallState.DIALING);
transaction.processTransaction(null);
// THEN
verify(mCallsManager, times(1))
- .transactionRequestNewFocusCall(eq(mMockCall1), eq(CallState.ACTIVE),
- isA(OutcomeReceiver.class));
+ .requestNewCallFocusAndVerify(eq(mMockCall1), isA(OutcomeReceiver.class));
}
@Test
- public void testAnswerCallTransaction() throws Exception {
+ public void testRequestNewCallFocusWithRingingCall() throws Exception {
// GIVEN
- AnswerCallTransaction transaction =
- new AnswerCallTransaction(mCallsManager, mMockCall1, 0);
+ RequestNewActiveCallTransaction transaction =
+ new RequestNewActiveCallTransaction(mCallsManager, mMockCall1);
// WHEN
+ when(mMockCall1.getState()).thenReturn(CallState.RINGING);
transaction.processTransaction(null);
// THEN
verify(mCallsManager, times(1))
- .transactionRequestNewFocusCall(eq(mMockCall1), eq(CallState.ANSWERED),
- isA(OutcomeReceiver.class));
+ .requestNewCallFocusAndVerify(eq(mMockCall1), isA(OutcomeReceiver.class));
+ }
+
+ @Test
+ public void testRequestNewCallFocusFailure() throws Exception {
+ // GIVEN
+ RequestNewActiveCallTransaction transaction =
+ new RequestNewActiveCallTransaction(mCallsManager, mMockCall1);
+
+ // WHEN
+ when(mMockCall1.getState()).thenReturn(CallState.DISCONNECTING);
+ when(mCallsManager.getActiveCall()).thenReturn(null);
+ transaction.processTransaction(null);
+
+ // THEN
+ verify(mCallsManager, times(0))
+ .requestNewCallFocusAndVerify( eq(mMockCall1), isA(OutcomeReceiver.class));
}
@Test
public void testTransactionalHoldActiveCallForNewCall() throws Exception {
// GIVEN
- HoldActiveCallForNewCallTransaction transaction =
- new HoldActiveCallForNewCallTransaction(mCallsManager, mMockCall1);
+ MaybeHoldCallForNewCallTransaction transaction =
+ new MaybeHoldCallForNewCallTransaction(mCallsManager, mMockCall1);
// WHEN
transaction.processTransaction(null);
diff --git a/tests/src/com/android/server/telecom/tests/VoipCallTransactionTest.java b/tests/src/com/android/server/telecom/tests/VoipCallTransactionTest.java
index 62b8bc4..e2c7b7b 100644
--- a/tests/src/com/android/server/telecom/tests/VoipCallTransactionTest.java
+++ b/tests/src/com/android/server/telecom/tests/VoipCallTransactionTest.java
@@ -211,7 +211,7 @@
subTransactions.add(t3);
CompletableFuture<String> exceptionFuture = new CompletableFuture<>();
OutcomeReceiver<VoipCallTransactionResult, CallException> outcomeReceiver =
- new OutcomeReceiver<VoipCallTransactionResult, CallException>() {
+ new OutcomeReceiver<>() {
@Override
public void onResult(VoipCallTransactionResult result) {
@@ -234,12 +234,20 @@
throws ExecutionException, InterruptedException, TimeoutException {
VoipCallTransaction t = new TestVoipCallTransaction("t", 10000L,
TestVoipCallTransaction.SUCCESS);
- CompletableFuture<VoipCallTransactionResult> resultFuture = new CompletableFuture<>();
+ CompletableFuture<String> exceptionFuture = new CompletableFuture<>();
OutcomeReceiver<VoipCallTransactionResult, CallException> outcomeReceiver =
- resultFuture::complete;
- mTransactionManager.addTransaction(t, outcomeReceiver);
- VoipCallTransactionResult result = resultFuture.get(7000L, TimeUnit.MILLISECONDS);
- assertEquals(VoipCallTransactionResult.RESULT_FAILED, result.getResult());
- assertTrue(result.getMessage().contains("timeout"));
+ new OutcomeReceiver<>() {
+ @Override
+ public void onResult(VoipCallTransactionResult result) {
+
+ }
+
+ @Override
+ public void onError(CallException e) {
+ exceptionFuture.complete(e.getMessage());
+ }
+ }; mTransactionManager.addTransaction(t, outcomeReceiver);
+ String message = exceptionFuture.get(7000L, TimeUnit.MILLISECONDS);
+ assertTrue(message.contains("timeout"));
}
}