Loading src/com/android/server/telecom/CallsManager.java +19 −21 Original line number Diff line number Diff line Loading @@ -5960,18 +5960,24 @@ public class CallsManager extends Call.ListenerBase } } // driver method to create and execute a new TransactionalFocusRequestCallback public void transactionRequestNewFocusCall(Call call, /** * Intended for ongoing or new calls that would like to go active/answered and need to * update the mConnectionSvrFocusMgr before setting the state */ public void transactionRequestNewFocusCall(Call call, int newCallState, OutcomeReceiver<Boolean, CallException> callback) { Log.d(this, "transactionRequestNewFocusCall"); PendingAction pendingAction = new ActionSetCallState(call, CallState.ACTIVE, PendingAction pendingAction = new ActionSetCallState(call, newCallState, "transactional ActionSetCallState"); mConnectionSvrFocusMgr .requestFocus(call, new TransactionalFocusRequestCallback(pendingAction, call, callback)); } // request a new call focus and ensure the request was successful /** * 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. */ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) public class TransactionalFocusRequestCallback implements ConnectionServiceFocusManager.RequestFocusCallback { Loading @@ -5990,26 +5996,18 @@ public class CallsManager extends Call.ListenerBase @Override public void onRequestFocusDone(ConnectionServiceFocusManager.CallFocus call) { Call currentCallFocus = (Call) mConnectionSvrFocusMgr.getCurrentFocusCall(); if (currentCallFocus == null) { Log.i(this, "TransactionalFocusRequestCallback: " + "currentCallFocus is null."); mCallback.onError(new CallException("currentCallFocus is null", // verify the update was successful before updating the state Log.i(this, "tFRC: currentCallFocus=[%s], targetFocus=[%s]", mTargetCallFocus, currentCallFocus); if (currentCallFocus == null || !currentCallFocus.getId().equals(mTargetCallFocus.getId())) { mCallback.onError(new CallException("failed to switch focus to requested call", CallException.CODE_CALL_CANNOT_BE_SET_TO_ACTIVE)); return; } Log.i(this, "TransactionalFocusRequestCallback: targetId=[%s], " + "currentId=[%s]", mTargetCallFocus.getId(), currentCallFocus.getId()); if (!currentCallFocus.getId().equals(mTargetCallFocus.getId())) { Log.i(this, "TransactionalFocusRequestCallback: " + "currentCallFocus is not equal to targetCallFocus."); mCallback.onError(new CallException("current focus is not target focus", CallException.CODE_CALL_CANNOT_BE_SET_TO_ACTIVE)); return; } mPendingAction.performAction(); mCallback.onResult(true); // at this point, we know the FocusManager is able to update successfully mPendingAction.performAction(); // set the call state mCallback.onResult(true); // complete the transaction } } Loading src/com/android/server/telecom/TransactionalServiceWrapper.java +32 −3 Original line number Diff line number Diff line Loading @@ -38,6 +38,7 @@ import androidx.annotation.VisibleForTesting; 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; Loading Loading @@ -67,7 +68,7 @@ public class TransactionalServiceWrapper implements // CallControl : Client (ex. voip app) --> Telecom public static final String SET_ACTIVE = "SetActive"; public static final String SET_INACTIVE = "SetInactive"; public static final String REJECT = "Reject"; public static final String ANSWER = "Answer"; public static final String DISCONNECT = "Disconnect"; public static final String START_STREAMING = "StartStreaming"; Loading @@ -75,7 +76,6 @@ public class TransactionalServiceWrapper implements public static final String ON_SET_ACTIVE = "onSetActive"; public static final String ON_SET_INACTIVE = "onSetInactive"; public static final String ON_ANSWER = "onAnswer"; public static final String ON_REJECT = "onReject"; public static final String ON_DISCONNECT = "onDisconnect"; public static final String ON_STREAMING_STARTED = "onStreamingStarted"; Loading Loading @@ -195,6 +195,17 @@ public class TransactionalServiceWrapper implements } } @Override public void answer(int videoState, String callId, android.os.ResultReceiver callback) throws RemoteException { try { Log.startSession("TSW.a"); createTransactions(callId, callback, ANSWER, videoState); } finally { Log.endSession(); } } @Override public void setInactive(String callId, android.os.ResultReceiver callback) throws RemoteException { Loading Loading @@ -238,6 +249,10 @@ public class TransactionalServiceWrapper implements case SET_ACTIVE: addTransactionsToManager(createSetActiveTransactions(call), callback); break; case ANSWER: addTransactionsToManager(createSetAnswerTransactions(call, (int) objects[0]), callback); break; case DISCONNECT: addTransactionsToManager(new EndCallTransaction(mCallsManager, (DisconnectCause) objects[0], call), callback); Loading Loading @@ -541,13 +556,27 @@ public class TransactionalServiceWrapper implements // add t1. hold potential active call transactions.add(new HoldActiveCallForNewCallTransaction(mCallsManager, call)); // add t2. answer current 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); } 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); } /*** ********************************************************************************************* ** FocusManager ** Loading src/com/android/server/telecom/voip/AnswerCallTransaction.java 0 → 100644 +75 −0 Original line number Diff line number Diff line /* * 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) { 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 src/com/android/server/telecom/voip/RequestFocusTransaction.java +3 −1 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ 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; Loading @@ -42,7 +43,8 @@ public class RequestFocusTransaction extends VoipCallTransaction { Log.d(TAG, "processTransaction"); CompletableFuture<VoipCallTransactionResult> future = new CompletableFuture<>(); mCallsManager.transactionRequestNewFocusCall(mCall, new OutcomeReceiver<>() { mCallsManager.transactionRequestNewFocusCall(mCall, CallState.ACTIVE, new OutcomeReceiver<>() { @Override public void onResult(Boolean result) { Log.d(TAG, "processTransaction: onResult"); Loading tests/src/com/android/server/telecom/tests/TransactionTests.java +18 −1 Original line number Diff line number Diff line Loading @@ -47,6 +47,7 @@ import com.android.server.telecom.ClockProxy; 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; Loading Loading @@ -152,7 +153,23 @@ public class TransactionTests extends TelecomTestCase { // THEN verify(mCallsManager, times(1)) .transactionRequestNewFocusCall(eq(mMockCall1), isA(OutcomeReceiver.class)); .transactionRequestNewFocusCall(eq(mMockCall1), eq(CallState.ACTIVE), isA(OutcomeReceiver.class)); } @Test public void testAnswerCallTransaction() throws Exception { // GIVEN AnswerCallTransaction transaction = new AnswerCallTransaction(mCallsManager, mMockCall1, 0); // WHEN transaction.processTransaction(null); // THEN verify(mCallsManager, times(1)) .transactionRequestNewFocusCall(eq(mMockCall1), eq(CallState.ANSWERED), isA(OutcomeReceiver.class)); } @Test Loading Loading
src/com/android/server/telecom/CallsManager.java +19 −21 Original line number Diff line number Diff line Loading @@ -5960,18 +5960,24 @@ public class CallsManager extends Call.ListenerBase } } // driver method to create and execute a new TransactionalFocusRequestCallback public void transactionRequestNewFocusCall(Call call, /** * Intended for ongoing or new calls that would like to go active/answered and need to * update the mConnectionSvrFocusMgr before setting the state */ public void transactionRequestNewFocusCall(Call call, int newCallState, OutcomeReceiver<Boolean, CallException> callback) { Log.d(this, "transactionRequestNewFocusCall"); PendingAction pendingAction = new ActionSetCallState(call, CallState.ACTIVE, PendingAction pendingAction = new ActionSetCallState(call, newCallState, "transactional ActionSetCallState"); mConnectionSvrFocusMgr .requestFocus(call, new TransactionalFocusRequestCallback(pendingAction, call, callback)); } // request a new call focus and ensure the request was successful /** * 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. */ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) public class TransactionalFocusRequestCallback implements ConnectionServiceFocusManager.RequestFocusCallback { Loading @@ -5990,26 +5996,18 @@ public class CallsManager extends Call.ListenerBase @Override public void onRequestFocusDone(ConnectionServiceFocusManager.CallFocus call) { Call currentCallFocus = (Call) mConnectionSvrFocusMgr.getCurrentFocusCall(); if (currentCallFocus == null) { Log.i(this, "TransactionalFocusRequestCallback: " + "currentCallFocus is null."); mCallback.onError(new CallException("currentCallFocus is null", // verify the update was successful before updating the state Log.i(this, "tFRC: currentCallFocus=[%s], targetFocus=[%s]", mTargetCallFocus, currentCallFocus); if (currentCallFocus == null || !currentCallFocus.getId().equals(mTargetCallFocus.getId())) { mCallback.onError(new CallException("failed to switch focus to requested call", CallException.CODE_CALL_CANNOT_BE_SET_TO_ACTIVE)); return; } Log.i(this, "TransactionalFocusRequestCallback: targetId=[%s], " + "currentId=[%s]", mTargetCallFocus.getId(), currentCallFocus.getId()); if (!currentCallFocus.getId().equals(mTargetCallFocus.getId())) { Log.i(this, "TransactionalFocusRequestCallback: " + "currentCallFocus is not equal to targetCallFocus."); mCallback.onError(new CallException("current focus is not target focus", CallException.CODE_CALL_CANNOT_BE_SET_TO_ACTIVE)); return; } mPendingAction.performAction(); mCallback.onResult(true); // at this point, we know the FocusManager is able to update successfully mPendingAction.performAction(); // set the call state mCallback.onResult(true); // complete the transaction } } Loading
src/com/android/server/telecom/TransactionalServiceWrapper.java +32 −3 Original line number Diff line number Diff line Loading @@ -38,6 +38,7 @@ import androidx.annotation.VisibleForTesting; 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; Loading Loading @@ -67,7 +68,7 @@ public class TransactionalServiceWrapper implements // CallControl : Client (ex. voip app) --> Telecom public static final String SET_ACTIVE = "SetActive"; public static final String SET_INACTIVE = "SetInactive"; public static final String REJECT = "Reject"; public static final String ANSWER = "Answer"; public static final String DISCONNECT = "Disconnect"; public static final String START_STREAMING = "StartStreaming"; Loading @@ -75,7 +76,6 @@ public class TransactionalServiceWrapper implements public static final String ON_SET_ACTIVE = "onSetActive"; public static final String ON_SET_INACTIVE = "onSetInactive"; public static final String ON_ANSWER = "onAnswer"; public static final String ON_REJECT = "onReject"; public static final String ON_DISCONNECT = "onDisconnect"; public static final String ON_STREAMING_STARTED = "onStreamingStarted"; Loading Loading @@ -195,6 +195,17 @@ public class TransactionalServiceWrapper implements } } @Override public void answer(int videoState, String callId, android.os.ResultReceiver callback) throws RemoteException { try { Log.startSession("TSW.a"); createTransactions(callId, callback, ANSWER, videoState); } finally { Log.endSession(); } } @Override public void setInactive(String callId, android.os.ResultReceiver callback) throws RemoteException { Loading Loading @@ -238,6 +249,10 @@ public class TransactionalServiceWrapper implements case SET_ACTIVE: addTransactionsToManager(createSetActiveTransactions(call), callback); break; case ANSWER: addTransactionsToManager(createSetAnswerTransactions(call, (int) objects[0]), callback); break; case DISCONNECT: addTransactionsToManager(new EndCallTransaction(mCallsManager, (DisconnectCause) objects[0], call), callback); Loading Loading @@ -541,13 +556,27 @@ public class TransactionalServiceWrapper implements // add t1. hold potential active call transactions.add(new HoldActiveCallForNewCallTransaction(mCallsManager, call)); // add t2. answer current 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); } 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); } /*** ********************************************************************************************* ** FocusManager ** Loading
src/com/android/server/telecom/voip/AnswerCallTransaction.java 0 → 100644 +75 −0 Original line number Diff line number Diff line /* * 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) { 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
src/com/android/server/telecom/voip/RequestFocusTransaction.java +3 −1 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ 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; Loading @@ -42,7 +43,8 @@ public class RequestFocusTransaction extends VoipCallTransaction { Log.d(TAG, "processTransaction"); CompletableFuture<VoipCallTransactionResult> future = new CompletableFuture<>(); mCallsManager.transactionRequestNewFocusCall(mCall, new OutcomeReceiver<>() { mCallsManager.transactionRequestNewFocusCall(mCall, CallState.ACTIVE, new OutcomeReceiver<>() { @Override public void onResult(Boolean result) { Log.d(TAG, "processTransaction: onResult"); Loading
tests/src/com/android/server/telecom/tests/TransactionTests.java +18 −1 Original line number Diff line number Diff line Loading @@ -47,6 +47,7 @@ import com.android.server.telecom.ClockProxy; 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; Loading Loading @@ -152,7 +153,23 @@ public class TransactionTests extends TelecomTestCase { // THEN verify(mCallsManager, times(1)) .transactionRequestNewFocusCall(eq(mMockCall1), isA(OutcomeReceiver.class)); .transactionRequestNewFocusCall(eq(mMockCall1), eq(CallState.ACTIVE), isA(OutcomeReceiver.class)); } @Test public void testAnswerCallTransaction() throws Exception { // GIVEN AnswerCallTransaction transaction = new AnswerCallTransaction(mCallsManager, mMockCall1, 0); // WHEN transaction.processTransaction(null); // THEN verify(mCallsManager, times(1)) .transactionRequestNewFocusCall(eq(mMockCall1), eq(CallState.ANSWERED), isA(OutcomeReceiver.class)); } @Test Loading