1 /* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.telecom.voip; 18 19 import static android.telecom.TelecomManager.TELECOM_TRANSACTION_SUCCESS; 20 import static android.telecom.CallException.CODE_OPERATION_TIMED_OUT; 21 22 import android.os.Bundle; 23 import android.os.RemoteException; 24 import android.os.ResultReceiver; 25 import android.telecom.CallAttributes; 26 import android.telecom.DisconnectCause; 27 import android.util.Log; 28 29 import com.android.internal.telecom.ICallEventCallback; 30 import com.android.server.telecom.TelecomSystem; 31 import com.android.server.telecom.TransactionalServiceWrapper; 32 33 import java.util.concurrent.CompletableFuture; 34 import java.util.concurrent.CompletionStage; 35 import java.util.concurrent.CountDownLatch; 36 import java.util.concurrent.TimeUnit; 37 38 /** 39 * SRP: using the ICallEventCallback binder, reach out to the client for the pending call event and 40 * get an acknowledgement that the call event can be completed. 41 */ 42 public class CallEventCallbackAckTransaction extends VoipCallTransaction { 43 private static final String TAG = CallEventCallbackAckTransaction.class.getSimpleName(); 44 private final ICallEventCallback mICallEventCallback; 45 private final String mAction; 46 private final String mCallId; 47 // optional values 48 private int mVideoState = CallAttributes.AUDIO_CALL; 49 private DisconnectCause mDisconnectCause = null; 50 51 private final VoipCallTransactionResult TRANSACTION_FAILED = new VoipCallTransactionResult( 52 CODE_OPERATION_TIMED_OUT, "failed to complete the operation before timeout"); 53 54 private static class AckResultReceiver extends ResultReceiver { 55 CountDownLatch mCountDownLatch; 56 AckResultReceiver(CountDownLatch latch)57 public AckResultReceiver(CountDownLatch latch) { 58 super(null); 59 mCountDownLatch = latch; 60 } 61 62 @Override onReceiveResult(int resultCode, Bundle resultData)63 protected void onReceiveResult(int resultCode, Bundle resultData) { 64 if (resultCode == TELECOM_TRANSACTION_SUCCESS) { 65 mCountDownLatch.countDown(); 66 } 67 } 68 } 69 CallEventCallbackAckTransaction(ICallEventCallback service, String action, String callId, TelecomSystem.SyncRoot lock)70 public CallEventCallbackAckTransaction(ICallEventCallback service, String action, 71 String callId, TelecomSystem.SyncRoot lock) { 72 super(lock); 73 mICallEventCallback = service; 74 mAction = action; 75 mCallId = callId; 76 } 77 78 CallEventCallbackAckTransaction(ICallEventCallback service, String action, String callId, int videoState, TelecomSystem.SyncRoot lock)79 public CallEventCallbackAckTransaction(ICallEventCallback service, String action, String callId, 80 int videoState, TelecomSystem.SyncRoot lock) { 81 super(lock); 82 mICallEventCallback = service; 83 mAction = action; 84 mCallId = callId; 85 mVideoState = videoState; 86 } 87 CallEventCallbackAckTransaction(ICallEventCallback service, String action, String callId, DisconnectCause cause, TelecomSystem.SyncRoot lock)88 public CallEventCallbackAckTransaction(ICallEventCallback service, String action, String callId, 89 DisconnectCause cause, TelecomSystem.SyncRoot lock) { 90 super(lock); 91 mICallEventCallback = service; 92 mAction = action; 93 mCallId = callId; 94 mDisconnectCause = cause; 95 } 96 97 98 @Override processTransaction(Void v)99 public CompletionStage<VoipCallTransactionResult> processTransaction(Void v) { 100 Log.d(TAG, "processTransaction"); 101 CountDownLatch latch = new CountDownLatch(1); 102 ResultReceiver receiver = new AckResultReceiver(latch); 103 104 try { 105 switch (mAction) { 106 case TransactionalServiceWrapper.ON_SET_INACTIVE: 107 mICallEventCallback.onSetInactive(mCallId, receiver); 108 break; 109 case TransactionalServiceWrapper.ON_DISCONNECT: 110 mICallEventCallback.onDisconnect(mCallId, mDisconnectCause, receiver); 111 break; 112 case TransactionalServiceWrapper.ON_SET_ACTIVE: 113 mICallEventCallback.onSetActive(mCallId, receiver); 114 break; 115 case TransactionalServiceWrapper.ON_ANSWER: 116 mICallEventCallback.onAnswer(mCallId, mVideoState, receiver); 117 break; 118 case TransactionalServiceWrapper.ON_STREAMING_STARTED: 119 mICallEventCallback.onCallStreamingStarted(mCallId, receiver); 120 break; 121 } 122 } catch (RemoteException remoteException) { 123 return CompletableFuture.completedFuture(TRANSACTION_FAILED); 124 } 125 126 try { 127 // wait for the client to ack that CallEventCallback 128 boolean success = latch.await(VoipCallTransaction.TIMEOUT_LIMIT, TimeUnit.MILLISECONDS); 129 if (!success) { 130 // client send onError and failed to complete transaction 131 Log.i(TAG, String.format("CallEventCallbackAckTransaction:" 132 + " client failed to complete the [%s] transaction", mAction)); 133 return CompletableFuture.completedFuture(TRANSACTION_FAILED); 134 } else { 135 // success 136 return CompletableFuture.completedFuture( 137 new VoipCallTransactionResult(VoipCallTransactionResult.RESULT_SUCCEED, 138 "success")); 139 } 140 } catch (InterruptedException ie) { 141 return CompletableFuture.completedFuture(TRANSACTION_FAILED); 142 } 143 } 144 } 145