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.CallException.CODE_OPERATION_TIMED_OUT; 20 21 import android.os.OutcomeReceiver; 22 import android.telecom.TelecomManager; 23 import android.telecom.CallException; 24 import android.util.Log; 25 26 import com.android.internal.annotations.VisibleForTesting; 27 28 import java.util.ArrayDeque; 29 import java.util.ArrayList; 30 import java.util.List; 31 import java.util.Queue; 32 33 public class TransactionManager { 34 private static final String TAG = "VoipCallTransactionManager"; 35 private static TransactionManager INSTANCE = null; 36 private static final Object sLock = new Object(); 37 private Queue<VoipCallTransaction> mTransactions; 38 private VoipCallTransaction mCurrentTransaction; 39 40 public interface TransactionCompleteListener { onTransactionCompleted(VoipCallTransactionResult result, String transactionName)41 void onTransactionCompleted(VoipCallTransactionResult result, String transactionName); onTransactionTimeout(String transactionName)42 void onTransactionTimeout(String transactionName); 43 } 44 TransactionManager()45 private TransactionManager() { 46 mTransactions = new ArrayDeque<>(); 47 mCurrentTransaction = null; 48 } 49 getInstance()50 public static TransactionManager getInstance() { 51 synchronized (sLock) { 52 if (INSTANCE == null) { 53 INSTANCE = new TransactionManager(); 54 } 55 } 56 return INSTANCE; 57 } 58 59 @VisibleForTesting getTestInstance()60 public static TransactionManager getTestInstance() { 61 return new TransactionManager(); 62 } 63 addTransaction(VoipCallTransaction transaction, OutcomeReceiver<VoipCallTransactionResult, CallException> receiver)64 public void addTransaction(VoipCallTransaction transaction, 65 OutcomeReceiver<VoipCallTransactionResult, CallException> receiver) { 66 synchronized (sLock) { 67 mTransactions.add(transaction); 68 } 69 transaction.setCompleteListener(new TransactionCompleteListener() { 70 @Override 71 public void onTransactionCompleted(VoipCallTransactionResult result, 72 String transactionName){ 73 Log.i(TAG, String.format("transaction %s completed: with result=[%d]", 74 transactionName, result.getResult())); 75 if (result.getResult() == TelecomManager.TELECOM_TRANSACTION_SUCCESS) { 76 receiver.onResult(result); 77 } else { 78 receiver.onError( 79 new CallException(result.getMessage(), 80 result.getResult())); 81 } 82 finishTransaction(); 83 } 84 85 @Override 86 public void onTransactionTimeout(String transactionName){ 87 Log.i(TAG, String.format("transaction %s timeout", transactionName)); 88 receiver.onError(new CallException(transactionName + " timeout", 89 CODE_OPERATION_TIMED_OUT)); 90 finishTransaction(); 91 } 92 }); 93 94 startTransactions(); 95 } 96 startTransactions()97 private void startTransactions() { 98 synchronized (sLock) { 99 if (mTransactions.isEmpty()) { 100 // No transaction waiting for process 101 return; 102 } 103 104 if (mCurrentTransaction != null) { 105 // Ongoing transaction 106 return; 107 } 108 mCurrentTransaction = mTransactions.poll(); 109 } 110 mCurrentTransaction.start(); 111 } 112 finishTransaction()113 private void finishTransaction() { 114 synchronized (sLock) { 115 mCurrentTransaction = null; 116 } 117 startTransactions(); 118 } 119 120 @VisibleForTesting clear()121 public void clear() { 122 List<VoipCallTransaction> pendingTransactions; 123 synchronized (sLock) { 124 pendingTransactions = new ArrayList<>(mTransactions); 125 } 126 for (VoipCallTransaction transaction : pendingTransactions) { 127 transaction.finish(); 128 } 129 } 130 } 131