Adds TransactionManager dumpsys info and cleanups
1) Adds new call sequencing flag for general DSDA development in Telecom
2) Adds a new Transaction History in TransactionManager
- Keeps track of when a transaction was added/started/completed
- prints the state of pending and completed transactions for
dumpsys purposes. Limits the completed transaction queue to 20
items for now.
- Modifies SerialTransaction to not remove sub-transactions so that
we can collect histry about the transaction when finished
3) Cleans up the visibility of some methods and adds flag propogation
changes for testing
Fixes: 315879576
Test: atest TelecomUnitTests:VoipCallTransactionTest
Change-Id: Ifac05d24f70c7e12b1dd5d8f89308d550d3e3ae5
diff --git a/flags/telecom_calls_manager_flags.aconfig b/flags/telecom_calls_manager_flags.aconfig
index 1a19480..a68042e 100644
--- a/flags/telecom_calls_manager_flags.aconfig
+++ b/flags/telecom_calls_manager_flags.aconfig
@@ -13,3 +13,10 @@
description: "This fix ensures the MO calls won't switch from Active to Quite b/c setDialing was not called"
bug: "309540769"
}
+
+flag {
+ name: "enable_call_sequencing"
+ namespace: "telecom"
+ description: "Enables simultaneous call sequencing for SIM PhoneAccounts"
+ bug: "297446980"
+}
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index de601a5..aa8eb57 100644
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -700,7 +700,8 @@
mCallLogManager = new CallLogManager(context, phoneAccountRegistrar, mMissedCallNotifier,
mAnomalyReporter, featureFlags);
mConnectionServiceRepository =
- new ConnectionServiceRepository(mPhoneAccountRegistrar, mContext, mLock, this);
+ new ConnectionServiceRepository(mPhoneAccountRegistrar, mContext, mLock, this,
+ featureFlags);
mInCallWakeLockController = inCallWakeLockControllerFactory.create(context, this);
mClockProxy = clockProxy;
mToastFactory = toastFactory;
@@ -5880,8 +5881,7 @@
return;
}
ConnectionServiceWrapper service = mConnectionServiceRepository.getService(
- phoneAccountHandle.getComponentName(), phoneAccountHandle.getUserHandle(),
- mFeatureFlags);
+ phoneAccountHandle.getComponentName(), phoneAccountHandle.getUserHandle());
if (service == null) {
Log.i(this, "Found no connection service.");
return;
@@ -5906,8 +5906,7 @@
return;
}
ConnectionServiceWrapper service = mConnectionServiceRepository.getService(
- phoneAccountHandle.getComponentName(), phoneAccountHandle.getUserHandle(),
- mFeatureFlags);
+ phoneAccountHandle.getComponentName(), phoneAccountHandle.getUserHandle());
if (service == null) {
Log.i(this, "Found no connection service.");
return;
diff --git a/src/com/android/server/telecom/ConnectionServiceRepository.java b/src/com/android/server/telecom/ConnectionServiceRepository.java
index d6a78d0..e4ed220 100644
--- a/src/com/android/server/telecom/ConnectionServiceRepository.java
+++ b/src/com/android/server/telecom/ConnectionServiceRepository.java
@@ -38,6 +38,7 @@
private final Context mContext;
private final TelecomSystem.SyncRoot mLock;
private final CallsManager mCallsManager;
+ private final FeatureFlags mFeatureFlags;
private final ServiceBinder.Listener<ConnectionServiceWrapper> mUnbindListener =
new ServiceBinder.Listener<ConnectionServiceWrapper>() {
@@ -54,18 +55,19 @@
PhoneAccountRegistrar phoneAccountRegistrar,
Context context,
TelecomSystem.SyncRoot lock,
- CallsManager callsManager) {
+ CallsManager callsManager,
+ FeatureFlags featureFlags) {
mPhoneAccountRegistrar = phoneAccountRegistrar;
mContext = context;
mLock = lock;
mCallsManager = callsManager;
+ mFeatureFlags = featureFlags;
}
@VisibleForTesting
public ConnectionServiceWrapper getService(
ComponentName componentName,
- UserHandle userHandle,
- FeatureFlags featureFlags) {
+ UserHandle userHandle) {
Pair<ComponentName, UserHandle> cacheKey = Pair.create(componentName, userHandle);
ConnectionServiceWrapper service = mServiceCache.get(cacheKey);
if (service == null) {
@@ -77,7 +79,7 @@
mContext,
mLock,
userHandle,
- featureFlags);
+ mFeatureFlags);
service.addListener(mUnbindListener);
mServiceCache.put(cacheKey, service);
}
diff --git a/src/com/android/server/telecom/ConnectionServiceWrapper.java b/src/com/android/server/telecom/ConnectionServiceWrapper.java
index 43ceff3..53da8ff 100644
--- a/src/com/android/server/telecom/ConnectionServiceWrapper.java
+++ b/src/com/android/server/telecom/ConnectionServiceWrapper.java
@@ -1351,7 +1351,6 @@
private final CallsManager mCallsManager;
private final AppOpsManager mAppOpsManager;
private final Context mContext;
- private final FeatureFlags mFlags;
private ConnectionServiceFocusManager.ConnectionServiceFocusListener mConnSvrFocusListener;
@@ -1386,7 +1385,6 @@
mCallsManager = callsManager;
mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
mContext = context;
- mFlags = featureFlags;
}
/** See {@link IConnectionService#addConnectionServiceAdapter}. */
@@ -2540,7 +2538,7 @@
isCallerConnectionManager = true;
}
ConnectionServiceWrapper service = mConnectionServiceRepository.getService(
- handle.getComponentName(), handle.getUserHandle(), mFlags);
+ handle.getComponentName(), handle.getUserHandle());
if (service != null && service != this) {
simServices.add(service);
} else {
diff --git a/src/com/android/server/telecom/CreateConnectionProcessor.java b/src/com/android/server/telecom/CreateConnectionProcessor.java
index f5b257d..bcb2d2f 100644
--- a/src/com/android/server/telecom/CreateConnectionProcessor.java
+++ b/src/com/android/server/telecom/CreateConnectionProcessor.java
@@ -244,7 +244,7 @@
Log.i(this, "Trying attempt %s", attempt);
PhoneAccountHandle phoneAccount = attempt.connectionManagerPhoneAccount;
mService = mRepository.getService(phoneAccount.getComponentName(),
- phoneAccount.getUserHandle(), mFlags);
+ phoneAccount.getUserHandle());
if (mService == null) {
Log.i(this, "Found no connection service for attempt %s", attempt);
attemptNextPhoneAccount();
@@ -260,7 +260,7 @@
PhoneAccountHandle remotePhoneAccount = attempt.targetPhoneAccount;
ConnectionServiceWrapper mRemoteService =
mRepository.getService(remotePhoneAccount.getComponentName(),
- remotePhoneAccount.getUserHandle(), mFlags);
+ remotePhoneAccount.getUserHandle());
if (mRemoteService == null) {
mCall.setConnectionService(mService);
} else {
diff --git a/src/com/android/server/telecom/TelecomServiceImpl.java b/src/com/android/server/telecom/TelecomServiceImpl.java
index b704d33..2932fb7 100644
--- a/src/com/android/server/telecom/TelecomServiceImpl.java
+++ b/src/com/android/server/telecom/TelecomServiceImpl.java
@@ -2027,6 +2027,11 @@
pw.increaseIndent();
reflectAndPrintFlagConfigs(pw);
pw.decreaseIndent();
+
+ pw.println("TransactionManager: ");
+ pw.increaseIndent();
+ TransactionManager.getInstance().dump(pw);
+ pw.decreaseIndent();
}
if (isTimeLineView) {
Log.dumpEventsTimeline(pw);
diff --git a/src/com/android/server/telecom/TransactionalServiceRepository.java b/src/com/android/server/telecom/TransactionalServiceRepository.java
index 15278e1..793840e 100644
--- a/src/com/android/server/telecom/TransactionalServiceRepository.java
+++ b/src/com/android/server/telecom/TransactionalServiceRepository.java
@@ -61,11 +61,11 @@
return service;
}
- public TransactionalServiceWrapper getTransactionalServiceWrapper(PhoneAccountHandle pah) {
+ private TransactionalServiceWrapper getTransactionalServiceWrapper(PhoneAccountHandle pah) {
return mServiceLookupTable.get(pah);
}
- public boolean hasExistingServiceWrapper(PhoneAccountHandle pah) {
+ private boolean hasExistingServiceWrapper(PhoneAccountHandle pah) {
return mServiceLookupTable.containsKey(pah);
}
diff --git a/src/com/android/server/telecom/TransactionalServiceWrapper.java b/src/com/android/server/telecom/TransactionalServiceWrapper.java
index 938ee58..98f2c9c 100644
--- a/src/com/android/server/telecom/TransactionalServiceWrapper.java
+++ b/src/com/android/server/telecom/TransactionalServiceWrapper.java
@@ -130,6 +130,7 @@
return mTransactionManager;
}
+ @VisibleForTesting
public PhoneAccountHandle getPhoneAccountHandle() {
return mPhoneAccountHandle;
}
@@ -166,7 +167,7 @@
return callCount;
}
- public void cleanupTransactionalServiceWrapper() {
+ private void cleanupTransactionalServiceWrapper() {
for (Call call : mTrackedCalls.values()) {
mCallsManager.markCallAsDisconnected(call,
new DisconnectCause(DisconnectCause.ERROR, "process died"));
@@ -179,7 +180,7 @@
** ICallControl: Client --> Server **
**********************************************************************************************
*/
- public final ICallControl mICallControl = new ICallControl.Stub() {
+ private final ICallControl mICallControl = new ICallControl.Stub() {
@Override
public void setActive(String callId, android.os.ResultReceiver callback)
throws RemoteException {
@@ -346,7 +347,7 @@
}
};
- public void addTransactionsToManager(VoipCallTransaction transaction,
+ private void addTransactionsToManager(VoipCallTransaction transaction,
ResultReceiver callback) {
Log.d(TAG, "addTransactionsToManager");
diff --git a/src/com/android/server/telecom/voip/ParallelTransaction.java b/src/com/android/server/telecom/voip/ParallelTransaction.java
index 6176087..621892a 100644
--- a/src/com/android/server/telecom/voip/ParallelTransaction.java
+++ b/src/com/android/server/telecom/voip/ParallelTransaction.java
@@ -34,6 +34,7 @@
@Override
public void start() {
+ if (mStats != null) mStats.markStarted();
// post timeout work
CompletableFuture<Void> future = new CompletableFuture<>();
mHandler.postDelayed(() -> future.complete(null), TIMEOUT_LIMIT);
@@ -44,7 +45,7 @@
if (mCompleteListener != null) {
mCompleteListener.onTransactionTimeout(mTransactionName);
}
- finish();
+ timeout();
return null;
}, new LoggedHandlerExecutor(mHandler, mTransactionName + "@" + hashCode()
+ ".s", mLock));
@@ -68,7 +69,7 @@
transactionName));
mCompleteListener.onTransactionCompleted(mainResult,
mTransactionName);
- finish();
+ finish(mainResult);
return null;
}, new LoggedHandlerExecutor(mHandler,
mTransactionName + "@" + hashCode()
@@ -91,7 +92,7 @@
transactionName));
mCompleteListener.onTransactionCompleted(mainResult,
mTransactionName);
- finish();
+ finish(mainResult);
return null;
}, new LoggedHandlerExecutor(mHandler,
mTransactionName + "@" + hashCode()
diff --git a/src/com/android/server/telecom/voip/SerialTransaction.java b/src/com/android/server/telecom/voip/SerialTransaction.java
index b35b471..7d5a178 100644
--- a/src/com/android/server/telecom/voip/SerialTransaction.java
+++ b/src/com/android/server/telecom/voip/SerialTransaction.java
@@ -21,6 +21,7 @@
import java.util.List;
import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.atomic.AtomicInteger;
/**
* A VoipCallTransaction implementation that its sub transactions will be executed in serial
@@ -37,6 +38,7 @@
@Override
public void start() {
+ if (mStats != null) mStats.markStarted();
// post timeout work
CompletableFuture<Void> future = new CompletableFuture<>();
mHandler.postDelayed(() -> future.complete(null), TIMEOUT_LIMIT);
@@ -47,7 +49,7 @@
if (mCompleteListener != null) {
mCompleteListener.onTransactionTimeout(mTransactionName);
}
- finish();
+ timeout();
return null;
}, new LoggedHandlerExecutor(mHandler, mTransactionName + "@" + hashCode()
+ ".s", mLock));
@@ -55,6 +57,7 @@
if (mSubTransactions != null && mSubTransactions.size() > 0) {
TransactionManager.TransactionCompleteListener subTransactionListener =
new TransactionManager.TransactionCompleteListener() {
+ private final AtomicInteger mTransactionIndex = new AtomicInteger(0);
@Override
public void onTransactionCompleted(VoipCallTransactionResult result,
@@ -71,14 +74,16 @@
transactionName));
mCompleteListener.onTransactionCompleted(mainResult,
mTransactionName);
- finish();
+ finish(mainResult);
return null;
}, new LoggedHandlerExecutor(mHandler,
mTransactionName + "@" + hashCode()
+ ".oTC", mLock));
} else {
- if (mSubTransactions.size() > 0) {
- VoipCallTransaction transaction = mSubTransactions.remove(0);
+ int currTransactionIndex = mTransactionIndex.incrementAndGet();
+ if (currTransactionIndex < mSubTransactions.size()) {
+ VoipCallTransaction transaction = mSubTransactions.get(
+ currTransactionIndex);
transaction.setCompleteListener(this);
transaction.start();
} else {
@@ -99,14 +104,14 @@
transactionName));
mCompleteListener.onTransactionCompleted(mainResult,
mTransactionName);
- finish();
+ finish(mainResult);
return null;
}, new LoggedHandlerExecutor(mHandler,
mTransactionName + "@" + hashCode()
+ ".oTT", mLock));
}
};
- VoipCallTransaction transaction = mSubTransactions.remove(0);
+ VoipCallTransaction transaction = mSubTransactions.get(0);
transaction.setCompleteListener(subTransactionListener);
transaction.start();
} else {
diff --git a/src/com/android/server/telecom/voip/TransactionManager.java b/src/com/android/server/telecom/voip/TransactionManager.java
index 228bdde..76d83cc 100644
--- a/src/com/android/server/telecom/voip/TransactionManager.java
+++ b/src/com/android/server/telecom/voip/TransactionManager.java
@@ -21,20 +21,25 @@
import android.os.OutcomeReceiver;
import android.telecom.TelecomManager;
import android.telecom.CallException;
+import android.util.IndentingPrintWriter;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
-
+import com.android.server.telecom.flags.Flags;
import java.util.ArrayDeque;
import java.util.ArrayList;
+import java.util.Deque;
import java.util.List;
+import java.util.Locale;
import java.util.Queue;
public class TransactionManager {
private static final String TAG = "VoipCallTransactionManager";
+ private static final int TRANSACTION_HISTORY_SIZE = 20;
private static TransactionManager INSTANCE = null;
private static final Object sLock = new Object();
- private Queue<VoipCallTransaction> mTransactions;
+ private final Queue<VoipCallTransaction> mTransactions;
+ private final Deque<VoipCallTransaction> mCompletedTransactions;
private VoipCallTransaction mCurrentTransaction;
public interface TransactionCompleteListener {
@@ -45,6 +50,10 @@
private TransactionManager() {
mTransactions = new ArrayDeque<>();
mCurrentTransaction = null;
+ if (Flags.enableCallSequencing()) {
+ mCompletedTransactions = new ArrayDeque<>();
+ } else
+ mCompletedTransactions = null;
}
public static TransactionManager getInstance() {
@@ -69,7 +78,7 @@
transaction.setCompleteListener(new TransactionCompleteListener() {
@Override
public void onTransactionCompleted(VoipCallTransactionResult result,
- String transactionName){
+ String transactionName) {
Log.i(TAG, String.format("transaction %s completed: with result=[%d]",
transactionName, result.getResult()));
if (result.getResult() == TelecomManager.TELECOM_TRANSACTION_SUCCESS) {
@@ -112,7 +121,10 @@
private void finishTransaction() {
synchronized (sLock) {
- mCurrentTransaction = null;
+ if (mCurrentTransaction != null) {
+ addTransactionToHistory(mCurrentTransaction);
+ mCurrentTransaction = null;
+ }
}
startTransactions();
}
@@ -123,8 +135,115 @@
synchronized (sLock) {
pendingTransactions = new ArrayList<>(mTransactions);
}
- for (VoipCallTransaction transaction : pendingTransactions) {
- transaction.finish();
+ for (VoipCallTransaction t : pendingTransactions) {
+ t.finish(new VoipCallTransactionResult(VoipCallTransactionResult.RESULT_FAILED,
+ "clear called"));
}
}
+
+ private void addTransactionToHistory(VoipCallTransaction t) {
+ if (!Flags.enableCallSequencing()) return;
+
+ mCompletedTransactions.add(t);
+ if (mCompletedTransactions.size() > TRANSACTION_HISTORY_SIZE) {
+ mCompletedTransactions.poll();
+ }
+ }
+
+ /**
+ * Called when the dumpsys is created for telecom to capture the current state.
+ */
+ public void dump(IndentingPrintWriter pw) {
+ if (!Flags.enableCallSequencing()) {
+ pw.println("<<Flag not enabled>>");
+ return;
+ }
+ synchronized (sLock) {
+ pw.println("Pending Transactions:");
+ pw.increaseIndent();
+ for (VoipCallTransaction t : mTransactions) {
+ printPendingTransactionStats(t, pw);
+ }
+ pw.decreaseIndent();
+
+ pw.println("Ongoing Transaction:");
+ pw.increaseIndent();
+ if (mCurrentTransaction != null) {
+ printPendingTransactionStats(mCurrentTransaction, pw);
+ }
+ pw.decreaseIndent();
+
+ pw.println("Completed Transactions:");
+ pw.increaseIndent();
+ for (VoipCallTransaction t : mCompletedTransactions) {
+ printCompleteTransactionStats(t, pw);
+ }
+ pw.decreaseIndent();
+ }
+ }
+
+ /**
+ * Recursively print the pending {@link VoipCallTransaction} stats for logging purposes.
+ * @param t The transaction that stats should be printed for
+ * @param pw The IndentingPrintWriter to print the result to
+ */
+ private void printPendingTransactionStats(VoipCallTransaction t, IndentingPrintWriter pw) {
+ VoipCallTransaction.Stats s = t.getStats();
+ if (s == null) {
+ pw.println(String.format(Locale.getDefault(), "%s: <NO STATS>", t.mTransactionName));
+ return;
+ }
+ pw.println(String.format(Locale.getDefault(),
+ "[%s] %s: (result=[%s]), (created -> now : [%+d] mS),"
+ + " (created -> started : [%+d] mS),"
+ + " (started -> now : [%+d] mS)",
+ s.addedTimeStamp, t.mTransactionName, parseTransactionResult(s),
+ s.measureTimeSinceCreatedMs(), s.measureCreatedToStartedMs(),
+ s.measureTimeSinceStartedMs()));
+
+ if (t.mSubTransactions == null || t.mSubTransactions.isEmpty()) {
+ return;
+ }
+ pw.increaseIndent();
+ for (VoipCallTransaction subTransaction : t.mSubTransactions) {
+ printPendingTransactionStats(subTransaction, pw);
+ }
+ pw.decreaseIndent();
+ }
+
+ /**
+ * Recursively print the complete Transaction stats for logging purposes.
+ * @param t The transaction that stats should be printed for
+ * @param pw The IndentingPrintWriter to print the result to
+ */
+ private void printCompleteTransactionStats(VoipCallTransaction t, IndentingPrintWriter pw) {
+ VoipCallTransaction.Stats s = t.getStats();
+ if (s == null) {
+ pw.println(String.format(Locale.getDefault(), "%s: <NO STATS>", t.mTransactionName));
+ return;
+ }
+ pw.println(String.format(Locale.getDefault(),
+ "[%s] %s: (result=[%s]), (created -> started : [%+d] mS), "
+ + "(started -> completed : [%+d] mS)",
+ s.addedTimeStamp, t.mTransactionName, parseTransactionResult(s),
+ s.measureCreatedToStartedMs(), s.measureStartedToCompletedMs()));
+
+ if (t.mSubTransactions == null || t.mSubTransactions.isEmpty()) {
+ return;
+ }
+ pw.increaseIndent();
+ for (VoipCallTransaction subTransaction : t.mSubTransactions) {
+ printCompleteTransactionStats(subTransaction, pw);
+ }
+ pw.decreaseIndent();
+ }
+
+ private String parseTransactionResult(VoipCallTransaction.Stats s) {
+ if (s.isTimedOut()) return "TIMED OUT";
+ if (s.getTransactionResult() == null) return "PENDING";
+ if (s.getTransactionResult().getResult() == VoipCallTransactionResult.RESULT_SUCCEED) {
+ return "SUCCESS";
+ }
+ return s.getTransactionResult().toString();
+ }
}
diff --git a/src/com/android/server/telecom/voip/VoipCallTransaction.java b/src/com/android/server/telecom/voip/VoipCallTransaction.java
index a952eb1..3c91158 100644
--- a/src/com/android/server/telecom/voip/VoipCallTransaction.java
+++ b/src/com/android/server/telecom/voip/VoipCallTransaction.java
@@ -22,23 +22,119 @@
import com.android.server.telecom.LoggedHandlerExecutor;
import com.android.server.telecom.TelecomSystem;
+import com.android.server.telecom.flags.Flags;
+import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
+import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Function;
public class VoipCallTransaction {
//TODO: add log events
protected static final long TIMEOUT_LIMIT = 5000L;
+
+ /**
+ * Tracks stats about a transaction for logging purposes.
+ */
+ public static class Stats {
+ // the logging visible timestamp for ease of debugging
+ public final LocalDateTime addedTimeStamp;
+ // the time in nS that the transaction was first created
+ private final long mCreatedTimeNs;
+ // the time that the transaction was started.
+ private long mStartedTimeNs = -1L;
+ // the time that the transaction was finished.
+ private long mFinishedTimeNs = -1L;
+ // If finished, did this transaction finish because it timed out?
+ private boolean mIsTimedOut = false;
+ private VoipCallTransactionResult mTransactionResult = null;
+
+ public Stats() {
+ addedTimeStamp = LocalDateTime.now();
+ mCreatedTimeNs = System.nanoTime();
+ }
+
+ /**
+ * Mark the transaction as started and record the time.
+ */
+ public void markStarted() {
+ if (mStartedTimeNs > -1) return;
+ mStartedTimeNs = System.nanoTime();
+ }
+
+ /**
+ * Mark the transaction as completed and record the time.
+ */
+ public void markComplete(boolean isTimedOut, VoipCallTransactionResult result) {
+ if (mFinishedTimeNs > -1) return;
+ mFinishedTimeNs = System.nanoTime();
+ mIsTimedOut = isTimedOut;
+ mTransactionResult = result;
+ }
+
+ /**
+ * @return Time in mS since the transaction was created.
+ */
+ public long measureTimeSinceCreatedMs() {
+ return TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - mCreatedTimeNs);
+ }
+
+ /**
+ * @return Time in mS between when transaction was created and when it was marked as
+ * started. Returns -1 if the transaction was not started yet.
+ */
+ public long measureCreatedToStartedMs() {
+ return mStartedTimeNs > 0 ?
+ TimeUnit.NANOSECONDS.toMillis(mStartedTimeNs - mCreatedTimeNs) : -1;
+ }
+
+ /**
+ * @return Time in mS since the transaction was marked started to the TransactionManager.
+ * Returns -1 if the transaction hasn't been started yet.
+ */
+ public long measureTimeSinceStartedMs() {
+ return mStartedTimeNs > 0 ?
+ TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - mStartedTimeNs) : -1;
+ }
+
+ /**
+ * @return Time in mS between when the transaction was marked as started and when it was
+ * marked as completed. Returns -1 if the transaction hasn't started or finished yet.
+ */
+ public long measureStartedToCompletedMs() {
+ return (mStartedTimeNs > 0 && mFinishedTimeNs > 0) ?
+ TimeUnit.NANOSECONDS.toMillis(mFinishedTimeNs - mStartedTimeNs) : -1;
+
+ }
+
+ /**
+ * @return true if this transaction completed due to timing out, false if the transaction
+ * hasn't completed yet or it completed and did not time out.
+ */
+ public boolean isTimedOut() {
+ return mIsTimedOut;
+ }
+
+ /**
+ * @return the result if the transaction completed, null if it timed out or hasn't completed
+ * yet.
+ */
+ public VoipCallTransactionResult getTransactionResult() {
+ return mTransactionResult;
+ }
+ }
+
protected final AtomicBoolean mCompleted = new AtomicBoolean(false);
- protected String mTransactionName = this.getClass().getSimpleName();
+ protected final String mTransactionName = this.getClass().getSimpleName();
private HandlerThread mHandlerThread;
protected Handler mHandler;
protected TransactionManager.TransactionCompleteListener mCompleteListener;
protected List<VoipCallTransaction> mSubTransactions;
protected TelecomSystem.SyncRoot mLock;
+ protected final Stats mStats;
public VoipCallTransaction(
List<VoipCallTransaction> subTransactions, TelecomSystem.SyncRoot lock) {
@@ -47,6 +143,7 @@
mHandlerThread.start();
mHandler = new Handler(mHandlerThread.getLooper());
mLock = lock;
+ mStats = Flags.enableCallSequencing() ? new Stats() : null;
}
public VoipCallTransaction(TelecomSystem.SyncRoot lock) {
@@ -54,6 +151,7 @@
}
public void start() {
+ if (mStats != null) mStats.markStarted();
// post timeout work
CompletableFuture<Void> future = new CompletableFuture<>();
mHandler.postDelayed(() -> future.complete(null), TIMEOUT_LIMIT);
@@ -64,7 +162,7 @@
if (mCompleteListener != null) {
mCompleteListener.onTransactionTimeout(mTransactionName);
}
- finish();
+ timeout();
return null;
}, new LoggedHandlerExecutor(mHandler, mTransactionName + "@" + hashCode()
+ ".s", mLock));
@@ -82,7 +180,7 @@
if (mCompleteListener != null) {
mCompleteListener.onTransactionCompleted(result, mTransactionName);
}
- finish();
+ finish(result);
return null;
}, executor)
.exceptionallyAsync((throwable -> {
@@ -100,11 +198,27 @@
mCompleteListener = listener;
}
- public void finish() {
+ public void timeout() {
+ finish(true, null);
+ }
+
+ public void finish(VoipCallTransactionResult result) {
+ finish(false, result);
+ }
+
+ public void finish(boolean isTimedOut, VoipCallTransactionResult result) {
+ if (mStats != null) mStats.markComplete(isTimedOut, result);
// finish all sub transactions
- if (mSubTransactions != null && mSubTransactions.size() > 0) {
- mSubTransactions.forEach(VoipCallTransaction::finish);
+ if (mSubTransactions != null && !mSubTransactions.isEmpty()) {
+ mSubTransactions.forEach( t -> t.finish(isTimedOut, result));
}
mHandlerThread.quit();
}
+
+ /**
+ * @return Stats related to this transaction if stats are enabled, null otherwise.
+ */
+ public Stats getStats() {
+ return mStats;
+ }
}
diff --git a/tests/src/com/android/server/telecom/tests/CreateConnectionProcessorTest.java b/tests/src/com/android/server/telecom/tests/CreateConnectionProcessorTest.java
index e973992..2f27bb5 100644
--- a/tests/src/com/android/server/telecom/tests/CreateConnectionProcessorTest.java
+++ b/tests/src/com/android/server/telecom/tests/CreateConnectionProcessorTest.java
@@ -838,9 +838,10 @@
private ConnectionServiceWrapper makeConnectionServiceWrapper() {
ConnectionServiceWrapper wrapper = mock(ConnectionServiceWrapper.class);
+
when(mMockConnectionServiceRepository.getService(
- eq(makeQuickConnectionServiceComponentName()),
- any(UserHandle.class), any(FeatureFlags.class))).thenReturn(wrapper);
+ eq(makeQuickConnectionServiceComponentName()), any(UserHandle.class)))
+ .thenReturn(wrapper);
return wrapper;
}