summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/core/java/com/android/server/location/contexthub/ContextHubService.java13
-rw-r--r--services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java575
-rw-r--r--services/core/java/com/android/server/location/contexthub/ContextHubTransactionManagerOld.java475
3 files changed, 847 insertions, 216 deletions
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubService.java b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
index 3f4a9bb4d864..ed69f7ad32f6 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubService.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
@@ -444,8 +444,17 @@ public class ContextHubService extends IContextHubService.Stub {
mSupportedContextHubPerms = hubInfo.second;
mContextHubInfoList = new ArrayList<>(mContextHubIdToInfoMap.values());
mClientManager = new ContextHubClientManager(mContext, mContextHubWrapper);
- mTransactionManager = new ContextHubTransactionManager(
- mContextHubWrapper, mClientManager, mNanoAppStateManager);
+
+ if (Flags.reduceLockingContextHubTransactionManager()) {
+ mTransactionManager =
+ new ContextHubTransactionManager(
+ mContextHubWrapper, mClientManager, mNanoAppStateManager);
+ } else {
+ mTransactionManager =
+ new ContextHubTransactionManagerOld(
+ mContextHubWrapper, mClientManager, mNanoAppStateManager);
+ }
+
mSensorPrivacyManagerInternal =
LocalServices.getService(SensorPrivacyManagerInternal.class);
return true;
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java b/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java
index 2a0b1afde27b..da31bf29a8e8 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java
@@ -26,6 +26,8 @@ import android.os.RemoteException;
import android.os.SystemClock;
import android.util.Log;
+import com.android.internal.annotations.GuardedBy;
+
import java.time.Duration;
import java.util.ArrayDeque;
import java.util.Collections;
@@ -43,8 +45,8 @@ import java.util.concurrent.atomic.AtomicInteger;
/**
* Manages transactions at the Context Hub Service.
- * <p>
- * This class maintains a queue of transaction requests made to the ContextHubService by clients,
+ *
+ * <p>This class maintains a queue of transaction requests made to the ContextHubService by clients,
* and executes them through the Context Hub. At any point in time, either the transaction queue is
* empty, or there is a pending transaction that is waiting for an asynchronous response from the
* hub. This class also handles synchronous errors and timeouts of each transaction.
@@ -52,66 +54,80 @@ import java.util.concurrent.atomic.AtomicInteger;
* @hide
*/
/* package */ class ContextHubTransactionManager {
- private static final String TAG = "ContextHubTransactionManager";
+ protected static final String TAG = "ContextHubTransactionManager";
public static final Duration RELIABLE_MESSAGE_TIMEOUT = Duration.ofSeconds(1);
public static final Duration RELIABLE_MESSAGE_DUPLICATE_DETECTION_TIMEOUT =
RELIABLE_MESSAGE_TIMEOUT.multipliedBy(3);
- private static final int MAX_PENDING_REQUESTS = 10000;
+ // TODO: b/362299144: When cleaning up the flag
+ // reduce_locking_context_hub_transaction_manager, change these to private
+ protected static final int MAX_PENDING_REQUESTS = 10000;
- private static final int RELIABLE_MESSAGE_MAX_NUM_RETRY = 3;
+ protected static final int RELIABLE_MESSAGE_MAX_NUM_RETRY = 3;
- private static final Duration RELIABLE_MESSAGE_RETRY_WAIT_TIME = Duration.ofMillis(250);
+ protected static final Duration RELIABLE_MESSAGE_RETRY_WAIT_TIME = Duration.ofMillis(250);
- private static final Duration RELIABLE_MESSAGE_MIN_WAIT_TIME = Duration.ofNanos(1000);
+ protected static final Duration RELIABLE_MESSAGE_MIN_WAIT_TIME = Duration.ofNanos(1000);
- private final IContextHubWrapper mContextHubProxy;
+ protected final IContextHubWrapper mContextHubProxy;
- private final ContextHubClientManager mClientManager;
+ protected final ContextHubClientManager mClientManager;
- private final NanoAppStateManager mNanoAppStateManager;
+ protected final NanoAppStateManager mNanoAppStateManager;
- private final ArrayDeque<ContextHubServiceTransaction> mTransactionQueue = new ArrayDeque<>();
+ @GuardedBy("mTransactionLock")
+ protected final ArrayDeque<ContextHubServiceTransaction> mTransactionQueue = new ArrayDeque<>();
- private final Map<Integer, ContextHubServiceTransaction> mReliableMessageTransactionMap =
+ @GuardedBy("mReliableMessageLock")
+ protected final Map<Integer, ContextHubServiceTransaction> mReliableMessageTransactionMap =
new HashMap<>();
/** A set of host endpoint IDs that have an active pending transaction. */
- private final Set<Short> mReliableMessageHostEndpointIdActiveSet = new HashSet<>();
+ @GuardedBy("mReliableMessageLock")
+ protected final Set<Short> mReliableMessageHostEndpointIdActiveSet = new HashSet<>();
- private final AtomicInteger mNextAvailableId = new AtomicInteger();
+ protected final AtomicInteger mNextAvailableId = new AtomicInteger();
/**
- * The next available message sequence number. We choose a random
- * number to start with to avoid collisions and limit the bound to
- * half of the max value to avoid overflow.
+ * The next available message sequence number. We choose a random number to start with to avoid
+ * collisions and limit the bound to half of the max value to avoid overflow.
*/
- private final AtomicInteger mNextAvailableMessageSequenceNumber =
+ protected final AtomicInteger mNextAvailableMessageSequenceNumber =
new AtomicInteger(new Random().nextInt(Integer.MAX_VALUE / 2));
/*
- * An executor and the future object for scheduling timeout timers and
+ * An executor and the future objects for scheduling timeout timers and
* for scheduling the processing of reliable message transactions.
*/
- private final ScheduledThreadPoolExecutor mExecutor = new ScheduledThreadPoolExecutor(1);
- private ScheduledFuture<?> mTimeoutFuture = null;
- private ScheduledFuture<?> mReliableMessageTransactionFuture = null;
+ protected final ScheduledThreadPoolExecutor mExecutor = new ScheduledThreadPoolExecutor(2);
+
+ @GuardedBy("mTransactionLock")
+ protected ScheduledFuture<?> mTimeoutFuture = null;
+
+ @GuardedBy("mReliableMessageLock")
+ protected ScheduledFuture<?> mReliableMessageTransactionFuture = null;
/*
* The list of previous transaction records.
*/
- private static final int NUM_TRANSACTION_RECORDS = 20;
- private final ConcurrentLinkedEvictingDeque<TransactionRecord> mTransactionRecordDeque =
+ protected static final int NUM_TRANSACTION_RECORDS = 20;
+ protected final ConcurrentLinkedEvictingDeque<TransactionRecord> mTransactionRecordDeque =
new ConcurrentLinkedEvictingDeque<>(NUM_TRANSACTION_RECORDS);
- /**
- * A container class to store a record of transactions.
+ /*
+ * Locks for synchronization of normal transactions separately from reliable message
+ * transactions.
*/
- private class TransactionRecord {
- private final String mTransaction;
- private final long mTimestamp;
+ protected final Object mTransactionLock = new Object();
+ protected final Object mReliableMessageLock = new Object();
+ protected final Object mTransactionRecordLock = new Object();
+
+ /** A container class to store a record of transactions. */
+ protected static class TransactionRecord {
+ protected final String mTransaction;
+ protected final long mTimestamp;
TransactionRecord(String transaction) {
mTransaction = transaction;
@@ -126,8 +142,18 @@ import java.util.concurrent.atomic.AtomicInteger;
}
}
+ /** Used when finishing a transaction. */
+ interface TransactionAcceptConditions {
+ /**
+ * Returns whether to accept the found transaction when receiving a response from the
+ * Context Hub.
+ */
+ boolean acceptTransaction(ContextHubServiceTransaction transaction);
+ }
+
/* package */ ContextHubTransactionManager(
- IContextHubWrapper contextHubProxy, ContextHubClientManager clientManager,
+ IContextHubWrapper contextHubProxy,
+ ContextHubClientManager clientManager,
NanoAppStateManager nanoAppStateManager) {
mContextHubProxy = contextHubProxy;
mClientManager = clientManager;
@@ -409,34 +435,47 @@ import java.util.concurrent.atomic.AtomicInteger;
/**
* Adds a new transaction to the queue.
- * <p>
- * If there was no pending transaction at the time, the transaction that was added will be
+ *
+ * <p>If there was no pending transaction at the time, the transaction that was added will be
* started in this method. If there were too many transactions in the queue, an exception will
* be thrown.
*
* @param transaction the transaction to add
- * @throws IllegalStateException if the queue is full
*/
/* package */
- synchronized void addTransaction(
- ContextHubServiceTransaction transaction) throws IllegalStateException {
+ void addTransaction(ContextHubServiceTransaction transaction) {
if (transaction == null) {
return;
}
- if (mTransactionQueue.size() >= MAX_PENDING_REQUESTS
- || mReliableMessageTransactionMap.size() >= MAX_PENDING_REQUESTS) {
- throw new IllegalStateException("Transaction queue is full (capacity = "
- + MAX_PENDING_REQUESTS + ")");
+ synchronized (mTransactionRecordLock) {
+ mTransactionRecordDeque.add(new TransactionRecord(transaction.toString()));
}
- mTransactionRecordDeque.add(new TransactionRecord(transaction.toString()));
if (Flags.reliableMessageRetrySupportService()
&& transaction.getTransactionType()
== ContextHubTransaction.TYPE_RELIABLE_MESSAGE) {
- mReliableMessageTransactionMap.put(transaction.getMessageSequenceNumber(), transaction);
+ synchronized (mReliableMessageLock) {
+ if (mReliableMessageTransactionMap.size() >= MAX_PENDING_REQUESTS) {
+ throw new IllegalStateException(
+ "Reliable message transaction queue is full "
+ + "(capacity = "
+ + MAX_PENDING_REQUESTS
+ + ")");
+ }
+ mReliableMessageTransactionMap.put(
+ transaction.getMessageSequenceNumber(), transaction);
+ }
mExecutor.execute(() -> processMessageTransactions());
- } else {
+ return;
+ }
+
+ synchronized (mTransactionLock) {
+ if (mTransactionQueue.size() >= MAX_PENDING_REQUESTS) {
+ throw new IllegalStateException(
+ "Transaction queue is full (capacity = " + MAX_PENDING_REQUESTS + ")");
+ }
+
mTransactionQueue.add(transaction);
if (mTransactionQueue.size() == 1) {
startNextTransaction();
@@ -448,62 +487,85 @@ import java.util.concurrent.atomic.AtomicInteger;
* Handles a transaction response from a Context Hub.
*
* @param transactionId the transaction ID of the response
- * @param success true if the transaction succeeded
+ * @param success true if the transaction succeeded
*/
/* package */
- synchronized void onTransactionResponse(int transactionId, boolean success) {
- ContextHubServiceTransaction transaction = mTransactionQueue.peek();
+ void onTransactionResponse(int transactionId, boolean success) {
+ TransactionAcceptConditions conditions =
+ transaction -> transaction.getTransactionId() == transactionId;
+ ContextHubServiceTransaction transaction = getTransactionAndHandleNext(conditions);
if (transaction == null) {
- Log.w(TAG, "Received unexpected transaction response (no transaction pending)");
- return;
- }
- if (transaction.getTransactionId() != transactionId) {
Log.w(TAG, "Received unexpected transaction response (expected ID = "
- + transaction.getTransactionId() + ", received ID = " + transactionId + ")");
+ + transactionId
+ + ", received ID = "
+ + transaction.getTransactionId()
+ + ")");
return;
}
- transaction.onTransactionComplete(success ? ContextHubTransaction.RESULT_SUCCESS :
- ContextHubTransaction.RESULT_FAILED_AT_HUB);
- removeTransactionAndStartNext();
+ synchronized (transaction) {
+ transaction.onTransactionComplete(
+ success
+ ? ContextHubTransaction.RESULT_SUCCESS
+ : ContextHubTransaction.RESULT_FAILED_AT_HUB);
+ transaction.setComplete();
+ }
}
+ /**
+ * Handles a message delivery response from a Context Hub.
+ *
+ * @param messageSequenceNumber the message sequence number of the response
+ * @param success true if the message was delivered successfully
+ */
/* package */
- synchronized void onMessageDeliveryResponse(int messageSequenceNumber, boolean success) {
+ void onMessageDeliveryResponse(int messageSequenceNumber, boolean success) {
if (!Flags.reliableMessageRetrySupportService()) {
- ContextHubServiceTransaction transaction = mTransactionQueue.peek();
+ TransactionAcceptConditions conditions =
+ transaction -> transaction.getTransactionType()
+ == ContextHubTransaction.TYPE_RELIABLE_MESSAGE
+ && transaction.getMessageSequenceNumber()
+ == messageSequenceNumber;
+ ContextHubServiceTransaction transaction = getTransactionAndHandleNext(conditions);
if (transaction == null) {
- Log.w(TAG, "Received unexpected transaction response (no transaction pending)");
+ Log.w(TAG, "Received unexpected message delivery response (expected"
+ + " message sequence number = "
+ + messageSequenceNumber
+ + ", received messageSequenceNumber = "
+ + messageSequenceNumber
+ + ")");
return;
}
- int transactionMessageSequenceNumber = transaction.getMessageSequenceNumber();
- if (transaction.getTransactionType() != ContextHubTransaction.TYPE_RELIABLE_MESSAGE
- || transactionMessageSequenceNumber != messageSequenceNumber) {
- Log.w(TAG, "Received unexpected message transaction response (expected message "
- + "sequence number = "
- + transaction.getMessageSequenceNumber()
- + ", received messageSequenceNumber = " + messageSequenceNumber + ")");
- return;
+ synchronized (transaction) {
+ transaction.onTransactionComplete(
+ success
+ ? ContextHubTransaction.RESULT_SUCCESS
+ : ContextHubTransaction.RESULT_FAILED_AT_HUB);
+ transaction.setComplete();
}
-
- transaction.onTransactionComplete(success ? ContextHubTransaction.RESULT_SUCCESS :
- ContextHubTransaction.RESULT_FAILED_AT_HUB);
- removeTransactionAndStartNext();
return;
}
- ContextHubServiceTransaction transaction =
- mReliableMessageTransactionMap.get(messageSequenceNumber);
- if (transaction == null) {
- Log.w(TAG, "Could not find reliable message transaction with "
- + "message sequence number = "
- + messageSequenceNumber);
- return;
+ ContextHubServiceTransaction transaction = null;
+ synchronized (mReliableMessageLock) {
+ transaction = mReliableMessageTransactionMap.get(messageSequenceNumber);
+ if (transaction == null) {
+ Log.w(
+ TAG,
+ "Could not find reliable message transaction with "
+ + "message sequence number = "
+ + messageSequenceNumber);
+ return;
+ }
+
+ removeMessageTransaction(transaction);
}
- completeMessageTransaction(transaction,
- success ? ContextHubTransaction.RESULT_SUCCESS
+ completeMessageTransaction(
+ transaction,
+ success
+ ? ContextHubTransaction.RESULT_SUCCESS
: ContextHubTransaction.RESULT_FAILED_AT_HUB);
mExecutor.execute(() -> processMessageTransactions());
}
@@ -514,77 +576,116 @@ import java.util.concurrent.atomic.AtomicInteger;
* @param nanoAppStateList the list of nanoapps included in the response
*/
/* package */
- synchronized void onQueryResponse(List<NanoAppState> nanoAppStateList) {
- ContextHubServiceTransaction transaction = mTransactionQueue.peek();
+ void onQueryResponse(List<NanoAppState> nanoAppStateList) {
+ TransactionAcceptConditions conditions = transaction ->
+ transaction.getTransactionType() == ContextHubTransaction.TYPE_QUERY_NANOAPPS;
+ ContextHubServiceTransaction transaction = getTransactionAndHandleNext(conditions);
if (transaction == null) {
- Log.w(TAG, "Received unexpected query response (no transaction pending)");
- return;
- }
- if (transaction.getTransactionType() != ContextHubTransaction.TYPE_QUERY_NANOAPPS) {
Log.w(TAG, "Received unexpected query response (expected " + transaction + ")");
return;
}
- transaction.onQueryResponse(ContextHubTransaction.RESULT_SUCCESS, nanoAppStateList);
- removeTransactionAndStartNext();
+ synchronized (transaction) {
+ transaction.onQueryResponse(ContextHubTransaction.RESULT_SUCCESS, nanoAppStateList);
+ transaction.setComplete();
+ }
}
- /**
- * Handles a hub reset event by stopping a pending transaction and starting the next.
- */
+ /** Handles a hub reset event by stopping a pending transaction and starting the next. */
/* package */
- synchronized void onHubReset() {
+ void onHubReset() {
if (Flags.reliableMessageRetrySupportService()) {
- Iterator<Map.Entry<Integer, ContextHubServiceTransaction>> iter =
- mReliableMessageTransactionMap.entrySet().iterator();
- while (iter.hasNext()) {
- completeMessageTransaction(iter.next().getValue(),
- ContextHubTransaction.RESULT_FAILED_AT_HUB, iter);
+ synchronized (mReliableMessageLock) {
+ Iterator<Map.Entry<Integer, ContextHubServiceTransaction>> iter =
+ mReliableMessageTransactionMap.entrySet().iterator();
+ while (iter.hasNext()) {
+ removeAndCompleteMessageTransaction(
+ iter.next().getValue(),
+ ContextHubTransaction.RESULT_FAILED_AT_HUB,
+ iter);
+ }
}
}
- ContextHubServiceTransaction transaction = mTransactionQueue.peek();
- if (transaction == null) {
- return;
+ synchronized (mTransactionLock) {
+ ContextHubServiceTransaction transaction = mTransactionQueue.peek();
+ if (transaction == null) {
+ return;
+ }
+
+ removeTransactionAndStartNext();
}
+ }
- removeTransactionAndStartNext();
+ /**
+ * This function also starts the next transaction and removes the active transaction from the
+ * queue. The caller should complete the transaction.
+ *
+ * <p>Returns the active transaction if the transaction queue is not empty, the transaction is
+ * not null, and the transaction matches the conditions.
+ */
+ private ContextHubServiceTransaction getTransactionAndHandleNext(
+ TransactionAcceptConditions conditions) {
+ ContextHubServiceTransaction transaction = null;
+ synchronized (mTransactionLock) {
+ transaction = mTransactionQueue.peek();
+ if (transaction == null
+ || (conditions != null && !conditions.acceptTransaction(transaction))) {
+ return null;
+ }
+
+ cancelTimeoutFuture();
+ mTransactionQueue.remove();
+ if (!mTransactionQueue.isEmpty()) {
+ startNextTransaction();
+ }
+ }
+ return transaction;
}
/**
* Pops the front transaction from the queue and starts the next pending transaction request.
- * <p>
- * Removing elements from the transaction queue must only be done through this method. When a
+ *
+ * <p>Removing elements from the transaction queue must only be done through this method. When a
* pending transaction is removed, the timeout timer is cancelled and the transaction is marked
* complete.
- * <p>
- * It is assumed that the transaction queue is non-empty when this method is invoked, and that
- * the caller has obtained a lock on this ContextHubTransactionManager object.
+ *
+ * <p>It is assumed that the transaction queue is non-empty when this method is invoked, and
+ * that the caller has obtained mTransactionLock.
*/
+ @GuardedBy("mTransactionLock")
private void removeTransactionAndStartNext() {
- if (mTimeoutFuture != null) {
- mTimeoutFuture.cancel(/* mayInterruptIfRunning= */ false);
- mTimeoutFuture = null;
- }
+ cancelTimeoutFuture();
ContextHubServiceTransaction transaction = mTransactionQueue.remove();
- transaction.setComplete();
+ synchronized (transaction) {
+ transaction.setComplete();
+ }
if (!mTransactionQueue.isEmpty()) {
startNextTransaction();
}
}
+ /** Cancels the timeout future. */
+ @GuardedBy("mTransactionLock")
+ private void cancelTimeoutFuture() {
+ if (mTimeoutFuture != null) {
+ mTimeoutFuture.cancel(/* mayInterruptIfRunning= */ false);
+ mTimeoutFuture = null;
+ }
+ }
+
/**
* Starts the next pending transaction request.
- * <p>
- * Starting new transactions must only be done through this method. This method continues to
+ *
+ * <p>Starting new transactions must only be done through this method. This method continues to
* process the transaction queue as long as there are pending requests, and no transaction is
* pending.
- * <p>
- * It is assumed that the caller has obtained a lock on this ContextHubTransactionManager
- * object.
+ *
+ * <p>It is assumed that the caller has obtained a lock on mTransactionLock.
*/
+ @GuardedBy("mTransactionLock")
private void startNextTransaction() {
int result = ContextHubTransaction.RESULT_FAILED_UNKNOWN;
while (result != ContextHubTransaction.RESULT_SUCCESS && !mTransactionQueue.isEmpty()) {
@@ -592,28 +693,36 @@ import java.util.concurrent.atomic.AtomicInteger;
result = transaction.onTransact();
if (result == ContextHubTransaction.RESULT_SUCCESS) {
- Runnable onTimeoutFunc = () -> {
- synchronized (this) {
- if (!transaction.isComplete()) {
- Log.d(TAG, transaction + " timed out");
- transaction.onTransactionComplete(
- ContextHubTransaction.RESULT_FAILED_TIMEOUT);
-
- removeTransactionAndStartNext();
- }
- }
- };
+ Runnable onTimeoutFunc =
+ () -> {
+ synchronized (transaction) {
+ if (!transaction.isComplete()) {
+ Log.d(TAG, transaction + " timed out");
+ transaction.onTransactionComplete(
+ ContextHubTransaction.RESULT_FAILED_TIMEOUT);
+ transaction.setComplete();
+ }
+ }
+
+ synchronized (mTransactionLock) {
+ removeTransactionAndStartNext();
+ }
+ };
long timeoutMs = transaction.getTimeout(TimeUnit.MILLISECONDS);
try {
- mTimeoutFuture = mExecutor.schedule(
- onTimeoutFunc, timeoutMs, TimeUnit.MILLISECONDS);
+ mTimeoutFuture =
+ mExecutor.schedule(onTimeoutFunc, timeoutMs, TimeUnit.MILLISECONDS);
} catch (Exception e) {
Log.e(TAG, "Error when schedule a timer", e);
}
} else {
- transaction.onTransactionComplete(
- ContextHubServiceUtil.toTransactionResult(result));
+ synchronized (transaction) {
+ transaction.onTransactionComplete(
+ ContextHubServiceUtil.toTransactionResult(result));
+ transaction.setComplete();
+ }
+
mTransactionQueue.remove();
}
}
@@ -621,81 +730,97 @@ import java.util.concurrent.atomic.AtomicInteger;
/**
* Processes message transactions, starting and completing them as needed.
- * <p>
- * This function is called when adding a message transaction or when a timer
- * expires for an existing message transaction's retry or timeout. The
- * internal processing loop will iterate at most twice as if one iteration
- * completes a transaction, the next iteration can only start new transactions.
- * If the first iteration does not complete any transaction, the loop will
- * only iterate once.
+ *
+ * <p>This function is called when adding a message transaction or when a timer expires for an
+ * existing message transaction's retry or timeout. The internal processing loop will iterate at
+ * most twice as if one iteration completes a transaction, the next iteration can only start new
+ * transactions. If the first iteration does not complete any transaction, the loop will only
+ * iterate once.
+ *
* <p>
*/
- private synchronized void processMessageTransactions() {
- if (!Flags.reliableMessageRetrySupportService()) {
- return;
- }
-
- if (mReliableMessageTransactionFuture != null) {
- mReliableMessageTransactionFuture.cancel(/* mayInterruptIfRunning= */ false);
- mReliableMessageTransactionFuture = null;
- }
+ private void processMessageTransactions() {
+ synchronized (mReliableMessageLock) {
+ if (!Flags.reliableMessageRetrySupportService()) {
+ return;
+ }
- long now = SystemClock.elapsedRealtimeNanos();
- long nextExecutionTime = Long.MAX_VALUE;
- boolean continueProcessing;
- do {
- continueProcessing = false;
- Iterator<Map.Entry<Integer, ContextHubServiceTransaction>> iter =
- mReliableMessageTransactionMap.entrySet().iterator();
- while (iter.hasNext()) {
- ContextHubServiceTransaction transaction = iter.next().getValue();
- short hostEndpointId = transaction.getHostEndpointId();
- int numCompletedStartCalls = transaction.getNumCompletedStartCalls();
- if (numCompletedStartCalls == 0
- && mReliableMessageHostEndpointIdActiveSet.contains(hostEndpointId)) {
- continue;
- }
+ if (mReliableMessageTransactionFuture != null) {
+ mReliableMessageTransactionFuture.cancel(/* mayInterruptIfRunning= */ false);
+ mReliableMessageTransactionFuture = null;
+ }
- long nextRetryTime = transaction.getNextRetryTime();
- long timeoutTime = transaction.getTimeoutTime();
- boolean transactionTimedOut = timeoutTime <= now;
- boolean transactionHitMaxRetries = nextRetryTime <= now
- && numCompletedStartCalls > RELIABLE_MESSAGE_MAX_NUM_RETRY;
- if (transactionTimedOut || transactionHitMaxRetries) {
- completeMessageTransaction(transaction,
- ContextHubTransaction.RESULT_FAILED_TIMEOUT, iter);
- continueProcessing = true;
- } else {
- if (nextRetryTime <= now || numCompletedStartCalls <= 0) {
- startMessageTransaction(transaction, now);
+ long now = SystemClock.elapsedRealtimeNanos();
+ long nextExecutionTime = Long.MAX_VALUE;
+ boolean continueProcessing;
+ do {
+ continueProcessing = false;
+ Iterator<Map.Entry<Integer, ContextHubServiceTransaction>> iter =
+ mReliableMessageTransactionMap.entrySet().iterator();
+ while (iter.hasNext()) {
+ ContextHubServiceTransaction transaction = iter.next().getValue();
+ short hostEndpointId = transaction.getHostEndpointId();
+ int numCompletedStartCalls = transaction.getNumCompletedStartCalls();
+ if (numCompletedStartCalls == 0
+ && mReliableMessageHostEndpointIdActiveSet.contains(hostEndpointId)) {
+ continue;
}
- nextExecutionTime = Math.min(nextExecutionTime,
- transaction.getNextRetryTime());
- nextExecutionTime = Math.min(nextExecutionTime,
- transaction.getTimeoutTime());
+ long nextRetryTime = transaction.getNextRetryTime();
+ long timeoutTime = transaction.getTimeoutTime();
+ boolean transactionTimedOut = timeoutTime <= now;
+ boolean transactionHitMaxRetries =
+ nextRetryTime <= now
+ && numCompletedStartCalls > RELIABLE_MESSAGE_MAX_NUM_RETRY;
+ if (transactionTimedOut || transactionHitMaxRetries) {
+ removeAndCompleteMessageTransaction(
+ transaction, ContextHubTransaction.RESULT_FAILED_TIMEOUT, iter);
+ continueProcessing = true;
+ } else {
+ if (nextRetryTime <= now || numCompletedStartCalls <= 0) {
+ startMessageTransaction(transaction, now);
+ }
+
+ nextExecutionTime =
+ Math.min(nextExecutionTime, transaction.getNextRetryTime());
+ nextExecutionTime =
+ Math.min(nextExecutionTime, transaction.getTimeoutTime());
+ }
}
+ } while (continueProcessing);
+
+ if (nextExecutionTime < Long.MAX_VALUE) {
+ mReliableMessageTransactionFuture =
+ mExecutor.schedule(
+ () -> processMessageTransactions(),
+ Math.max(
+ nextExecutionTime - SystemClock.elapsedRealtimeNanos(),
+ RELIABLE_MESSAGE_MIN_WAIT_TIME.toNanos()),
+ TimeUnit.NANOSECONDS);
}
- } while (continueProcessing);
-
- if (nextExecutionTime < Long.MAX_VALUE) {
- mReliableMessageTransactionFuture = mExecutor.schedule(
- () -> processMessageTransactions(),
- Math.max(nextExecutionTime - SystemClock.elapsedRealtimeNanos(),
- RELIABLE_MESSAGE_MIN_WAIT_TIME.toNanos()),
- TimeUnit.NANOSECONDS);
}
}
/**
- * Completes a message transaction and removes it from the reliable message map.
+ * Completes a message transaction.
*
* @param transaction The transaction to complete.
* @param result The result code.
*/
- private void completeMessageTransaction(ContextHubServiceTransaction transaction,
- @ContextHubTransaction.Result int result) {
- completeMessageTransaction(transaction, result, /* iter= */ null);
+ private void completeMessageTransaction(
+ ContextHubServiceTransaction transaction, @ContextHubTransaction.Result int result) {
+ synchronized (transaction) {
+ transaction.onTransactionComplete(result);
+ transaction.setComplete();
+ }
+
+ Log.d(
+ TAG,
+ "Successfully completed reliable message transaction with "
+ + "message sequence number = "
+ + transaction.getMessageSequenceNumber()
+ + " and result = "
+ + result);
}
/**
@@ -705,25 +830,41 @@ import java.util.concurrent.atomic.AtomicInteger;
* @param result The result code.
* @param iter The iterator for the reliable message map - used to remove the message directly.
*/
- private void completeMessageTransaction(ContextHubServiceTransaction transaction,
+ @GuardedBy("mReliableMessageLock")
+ private void removeAndCompleteMessageTransaction(
+ ContextHubServiceTransaction transaction,
@ContextHubTransaction.Result int result,
Iterator<Map.Entry<Integer, ContextHubServiceTransaction>> iter) {
- transaction.onTransactionComplete(result);
+ removeMessageTransaction(transaction, iter);
+ completeMessageTransaction(transaction, result);
+ }
+ /**
+ * Removes a message transaction from the reliable message map.
+ *
+ * @param transaction The transaction to remove.
+ */
+ @GuardedBy("mReliableMessageLock")
+ private void removeMessageTransaction(ContextHubServiceTransaction transaction) {
+ removeMessageTransaction(transaction, /* iter= */ null);
+ }
+
+ /**
+ * Removes a message transaction from the reliable message map.
+ *
+ * @param transaction The transaction to remove.
+ * @param iter The iterator for the reliable message map - used to remove the message directly.
+ */
+ @GuardedBy("mReliableMessageLock")
+ private void removeMessageTransaction(
+ ContextHubServiceTransaction transaction,
+ Iterator<Map.Entry<Integer, ContextHubServiceTransaction>> iter) {
if (iter == null) {
mReliableMessageTransactionMap.remove(transaction.getMessageSequenceNumber());
} else {
iter.remove();
}
mReliableMessageHostEndpointIdActiveSet.remove(transaction.getHostEndpointId());
-
- Log.d(
- TAG,
- "Successfully completed reliable message transaction with "
- + "message sequence number = "
- + transaction.getMessageSequenceNumber()
- + " and result = "
- + result);
}
/**
@@ -732,24 +873,25 @@ import java.util.concurrent.atomic.AtomicInteger;
* @param transaction The transaction to start.
* @param now The now time.
*/
+ @GuardedBy("mReliableMessageLock")
private void startMessageTransaction(ContextHubServiceTransaction transaction, long now) {
int numCompletedStartCalls = transaction.getNumCompletedStartCalls();
@ContextHubTransaction.Result int result = transaction.onTransact();
if (result == ContextHubTransaction.RESULT_SUCCESS) {
- Log.d(
- TAG,
- "Successfully "
- + (numCompletedStartCalls == 0 ? "started" : "retried")
- + " reliable message transaction with message sequence number = "
- + transaction.getMessageSequenceNumber());
+ Log.d(
+ TAG,
+ "Successfully "
+ + (numCompletedStartCalls == 0 ? "started" : "retried")
+ + " reliable message transaction with message sequence number = "
+ + transaction.getMessageSequenceNumber());
} else {
- Log.w(
- TAG,
- "Could not start reliable message transaction with "
- + "message sequence number = "
- + transaction.getMessageSequenceNumber()
- + ", result = "
- + result);
+ Log.w(
+ TAG,
+ "Could not start reliable message transaction with "
+ + "message sequence number = "
+ + transaction.getMessageSequenceNumber()
+ + ", result = "
+ + result);
}
transaction.setNextRetryTime(now + RELIABLE_MESSAGE_RETRY_WAIT_TIME.toNanos());
@@ -788,17 +930,19 @@ import java.util.concurrent.atomic.AtomicInteger;
public String toString() {
StringBuilder sb = new StringBuilder();
int i = 0;
- synchronized (this) {
- for (ContextHubServiceTransaction transaction: mTransactionQueue) {
+ synchronized (mTransactionLock) {
+ for (ContextHubServiceTransaction transaction : mTransactionQueue) {
sb.append(i);
sb.append(": ");
sb.append(transaction.toString());
sb.append("\n");
++i;
}
+ }
- if (Flags.reliableMessageRetrySupportService()) {
- for (ContextHubServiceTransaction transaction:
+ if (Flags.reliableMessageRetrySupportService()) {
+ synchronized (mReliableMessageLock) {
+ for (ContextHubServiceTransaction transaction :
mReliableMessageTransactionMap.values()) {
sb.append(i);
sb.append(": ");
@@ -807,7 +951,9 @@ import java.util.concurrent.atomic.AtomicInteger;
++i;
}
}
+ }
+ synchronized (mTransactionRecordLock) {
sb.append("Transaction History:\n");
Iterator<TransactionRecord> iterator = mTransactionRecordDeque.descendingIterator();
while (iterator.hasNext()) {
@@ -815,6 +961,7 @@ import java.util.concurrent.atomic.AtomicInteger;
sb.append("\n");
}
}
+
return sb.toString();
}
}
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManagerOld.java b/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManagerOld.java
new file mode 100644
index 000000000000..a67fa308a6ea
--- /dev/null
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManagerOld.java
@@ -0,0 +1,475 @@
+/*
+ * Copyright (C) 2024 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.location.contexthub;
+
+import android.chre.flags.Flags;
+import android.hardware.location.ContextHubTransaction;
+import android.hardware.location.IContextHubTransactionCallback;
+import android.hardware.location.NanoAppBinary;
+import android.hardware.location.NanoAppMessage;
+import android.hardware.location.NanoAppState;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.util.Log;
+
+import java.time.Duration;
+import java.util.ArrayDeque;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.Set;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Manages transactions at the Context Hub Service.
+ *
+ * <p>This class maintains a queue of transaction requests made to the ContextHubService by clients,
+ * and executes them through the Context Hub. At any point in time, either the transaction queue is
+ * empty, or there is a pending transaction that is waiting for an asynchronous response from the
+ * hub. This class also handles synchronous errors and timeouts of each transaction.
+ *
+ * <p>This is the old version of ContextHubTransactionManager that uses global synchronization
+ * instead of individual locks. This will be deleted when the
+ * reduce_locking_context_hub_transaction_manager flag is cleaned up.
+ *
+ * @hide
+ */
+/* package */ class ContextHubTransactionManagerOld extends ContextHubTransactionManager {
+ /* package */ ContextHubTransactionManagerOld(
+ IContextHubWrapper contextHubProxy,
+ ContextHubClientManager clientManager,
+ NanoAppStateManager nanoAppStateManager) {
+ super(contextHubProxy, clientManager, nanoAppStateManager);
+ }
+
+ /**
+ * Adds a new transaction to the queue.
+ *
+ * <p>If there was no pending transaction at the time, the transaction that was added will be
+ * started in this method. If there were too many transactions in the queue, an exception will
+ * be thrown.
+ *
+ * @param transaction the transaction to add
+ * @throws IllegalStateException if the queue is full
+ */
+ /* package */
+ @Override
+ synchronized void addTransaction(ContextHubServiceTransaction transaction)
+ throws IllegalStateException {
+ if (transaction == null) {
+ return;
+ }
+
+ if (mTransactionQueue.size() >= MAX_PENDING_REQUESTS
+ || mReliableMessageTransactionMap.size() >= MAX_PENDING_REQUESTS) {
+ throw new IllegalStateException(
+ "Transaction queue is full (capacity = " + MAX_PENDING_REQUESTS + ")");
+ }
+
+ mTransactionRecordDeque.add(new TransactionRecord(transaction.toString()));
+ if (Flags.reliableMessageRetrySupportService()
+ && transaction.getTransactionType()
+ == ContextHubTransaction.TYPE_RELIABLE_MESSAGE) {
+ mReliableMessageTransactionMap.put(transaction.getMessageSequenceNumber(), transaction);
+ mExecutor.execute(() -> processMessageTransactions());
+ } else {
+ mTransactionQueue.add(transaction);
+ if (mTransactionQueue.size() == 1) {
+ startNextTransaction();
+ }
+ }
+ }
+
+ /**
+ * Handles a transaction response from a Context Hub.
+ *
+ * @param transactionId the transaction ID of the response
+ * @param success true if the transaction succeeded
+ */
+ /* package */
+ @Override
+ synchronized void onTransactionResponse(int transactionId, boolean success) {
+ ContextHubServiceTransaction transaction = mTransactionQueue.peek();
+ if (transaction == null) {
+ Log.w(TAG, "Received unexpected transaction response (no transaction pending)");
+ return;
+ }
+ if (transaction.getTransactionId() != transactionId) {
+ Log.w(
+ TAG,
+ "Received unexpected transaction response (expected ID = "
+ + transaction.getTransactionId()
+ + ", received ID = "
+ + transactionId
+ + ")");
+ return;
+ }
+
+ transaction.onTransactionComplete(
+ success
+ ? ContextHubTransaction.RESULT_SUCCESS
+ : ContextHubTransaction.RESULT_FAILED_AT_HUB);
+ removeTransactionAndStartNext();
+ }
+
+ /* package */
+ @Override
+ synchronized void onMessageDeliveryResponse(int messageSequenceNumber, boolean success) {
+ if (!Flags.reliableMessageRetrySupportService()) {
+ ContextHubServiceTransaction transaction = mTransactionQueue.peek();
+ if (transaction == null) {
+ Log.w(TAG, "Received unexpected transaction response (no transaction pending)");
+ return;
+ }
+
+ int transactionMessageSequenceNumber = transaction.getMessageSequenceNumber();
+ if (transaction.getTransactionType() != ContextHubTransaction.TYPE_RELIABLE_MESSAGE
+ || transactionMessageSequenceNumber != messageSequenceNumber) {
+ Log.w(
+ TAG,
+ "Received unexpected message transaction response (expected message "
+ + "sequence number = "
+ + transaction.getMessageSequenceNumber()
+ + ", received messageSequenceNumber = "
+ + messageSequenceNumber
+ + ")");
+ return;
+ }
+
+ transaction.onTransactionComplete(
+ success
+ ? ContextHubTransaction.RESULT_SUCCESS
+ : ContextHubTransaction.RESULT_FAILED_AT_HUB);
+ removeTransactionAndStartNext();
+ return;
+ }
+
+ ContextHubServiceTransaction transaction =
+ mReliableMessageTransactionMap.get(messageSequenceNumber);
+ if (transaction == null) {
+ Log.w(
+ TAG,
+ "Could not find reliable message transaction with "
+ + "message sequence number = "
+ + messageSequenceNumber);
+ return;
+ }
+
+ completeMessageTransaction(
+ transaction,
+ success
+ ? ContextHubTransaction.RESULT_SUCCESS
+ : ContextHubTransaction.RESULT_FAILED_AT_HUB);
+ mExecutor.execute(() -> processMessageTransactions());
+ }
+
+ /**
+ * Handles a query response from a Context Hub.
+ *
+ * @param nanoAppStateList the list of nanoapps included in the response
+ */
+ /* package */
+ @Override
+ synchronized void onQueryResponse(List<NanoAppState> nanoAppStateList) {
+ ContextHubServiceTransaction transaction = mTransactionQueue.peek();
+ if (transaction == null) {
+ Log.w(TAG, "Received unexpected query response (no transaction pending)");
+ return;
+ }
+ if (transaction.getTransactionType() != ContextHubTransaction.TYPE_QUERY_NANOAPPS) {
+ Log.w(TAG, "Received unexpected query response (expected " + transaction + ")");
+ return;
+ }
+
+ transaction.onQueryResponse(ContextHubTransaction.RESULT_SUCCESS, nanoAppStateList);
+ removeTransactionAndStartNext();
+ }
+
+ /** Handles a hub reset event by stopping a pending transaction and starting the next. */
+ /* package */
+ @Override
+ synchronized void onHubReset() {
+ if (Flags.reliableMessageRetrySupportService()) {
+ Iterator<Map.Entry<Integer, ContextHubServiceTransaction>> iter =
+ mReliableMessageTransactionMap.entrySet().iterator();
+ while (iter.hasNext()) {
+ completeMessageTransaction(
+ iter.next().getValue(), ContextHubTransaction.RESULT_FAILED_AT_HUB, iter);
+ }
+ }
+
+ ContextHubServiceTransaction transaction = mTransactionQueue.peek();
+ if (transaction == null) {
+ return;
+ }
+
+ removeTransactionAndStartNext();
+ }
+
+ /**
+ * Pops the front transaction from the queue and starts the next pending transaction request.
+ *
+ * <p>Removing elements from the transaction queue must only be done through this method. When a
+ * pending transaction is removed, the timeout timer is cancelled and the transaction is marked
+ * complete.
+ *
+ * <p>It is assumed that the transaction queue is non-empty when this method is invoked, and
+ * that the caller has obtained a lock on this ContextHubTransactionManager object.
+ */
+ private void removeTransactionAndStartNext() {
+ if (mTimeoutFuture != null) {
+ mTimeoutFuture.cancel(/* mayInterruptIfRunning= */ false);
+ mTimeoutFuture = null;
+ }
+
+ ContextHubServiceTransaction transaction = mTransactionQueue.remove();
+ transaction.setComplete();
+
+ if (!mTransactionQueue.isEmpty()) {
+ startNextTransaction();
+ }
+ }
+
+ /**
+ * Starts the next pending transaction request.
+ *
+ * <p>Starting new transactions must only be done through this method. This method continues to
+ * process the transaction queue as long as there are pending requests, and no transaction is
+ * pending.
+ *
+ * <p>It is assumed that the caller has obtained a lock on this ContextHubTransactionManager
+ * object.
+ */
+ private void startNextTransaction() {
+ int result = ContextHubTransaction.RESULT_FAILED_UNKNOWN;
+ while (result != ContextHubTransaction.RESULT_SUCCESS && !mTransactionQueue.isEmpty()) {
+ ContextHubServiceTransaction transaction = mTransactionQueue.peek();
+ result = transaction.onTransact();
+
+ if (result == ContextHubTransaction.RESULT_SUCCESS) {
+ Runnable onTimeoutFunc =
+ () -> {
+ synchronized (this) {
+ if (!transaction.isComplete()) {
+ Log.d(TAG, transaction + " timed out");
+ transaction.onTransactionComplete(
+ ContextHubTransaction.RESULT_FAILED_TIMEOUT);
+
+ removeTransactionAndStartNext();
+ }
+ }
+ };
+
+ long timeoutMs = transaction.getTimeout(TimeUnit.MILLISECONDS);
+ try {
+ mTimeoutFuture =
+ mExecutor.schedule(onTimeoutFunc, timeoutMs, TimeUnit.MILLISECONDS);
+ } catch (Exception e) {
+ Log.e(TAG, "Error when schedule a timer", e);
+ }
+ } else {
+ transaction.onTransactionComplete(
+ ContextHubServiceUtil.toTransactionResult(result));
+ mTransactionQueue.remove();
+ }
+ }
+ }
+
+ /**
+ * Processes message transactions, starting and completing them as needed.
+ *
+ * <p>This function is called when adding a message transaction or when a timer expires for an
+ * existing message transaction's retry or timeout. The internal processing loop will iterate at
+ * most twice as if one iteration completes a transaction, the next iteration can only start new
+ * transactions. If the first iteration does not complete any transaction, the loop will only
+ * iterate once.
+ *
+ * <p>
+ */
+ private synchronized void processMessageTransactions() {
+ if (!Flags.reliableMessageRetrySupportService()) {
+ return;
+ }
+
+ if (mReliableMessageTransactionFuture != null) {
+ mReliableMessageTransactionFuture.cancel(/* mayInterruptIfRunning= */ false);
+ mReliableMessageTransactionFuture = null;
+ }
+
+ long now = SystemClock.elapsedRealtimeNanos();
+ long nextExecutionTime = Long.MAX_VALUE;
+ boolean continueProcessing;
+ do {
+ continueProcessing = false;
+ Iterator<Map.Entry<Integer, ContextHubServiceTransaction>> iter =
+ mReliableMessageTransactionMap.entrySet().iterator();
+ while (iter.hasNext()) {
+ ContextHubServiceTransaction transaction = iter.next().getValue();
+ short hostEndpointId = transaction.getHostEndpointId();
+ int numCompletedStartCalls = transaction.getNumCompletedStartCalls();
+ if (numCompletedStartCalls == 0
+ && mReliableMessageHostEndpointIdActiveSet.contains(hostEndpointId)) {
+ continue;
+ }
+
+ long nextRetryTime = transaction.getNextRetryTime();
+ long timeoutTime = transaction.getTimeoutTime();
+ boolean transactionTimedOut = timeoutTime <= now;
+ boolean transactionHitMaxRetries =
+ nextRetryTime <= now
+ && numCompletedStartCalls > RELIABLE_MESSAGE_MAX_NUM_RETRY;
+ if (transactionTimedOut || transactionHitMaxRetries) {
+ completeMessageTransaction(
+ transaction, ContextHubTransaction.RESULT_FAILED_TIMEOUT, iter);
+ continueProcessing = true;
+ } else {
+ if (nextRetryTime <= now || numCompletedStartCalls <= 0) {
+ startMessageTransaction(transaction, now);
+ }
+
+ nextExecutionTime = Math.min(nextExecutionTime, transaction.getNextRetryTime());
+ nextExecutionTime = Math.min(nextExecutionTime, transaction.getTimeoutTime());
+ }
+ }
+ } while (continueProcessing);
+
+ if (nextExecutionTime < Long.MAX_VALUE) {
+ mReliableMessageTransactionFuture =
+ mExecutor.schedule(
+ () -> processMessageTransactions(),
+ Math.max(
+ nextExecutionTime - SystemClock.elapsedRealtimeNanos(),
+ RELIABLE_MESSAGE_MIN_WAIT_TIME.toNanos()),
+ TimeUnit.NANOSECONDS);
+ }
+ }
+
+ /**
+ * Completes a message transaction and removes it from the reliable message map.
+ *
+ * @param transaction The transaction to complete.
+ * @param result The result code.
+ */
+ private void completeMessageTransaction(
+ ContextHubServiceTransaction transaction, @ContextHubTransaction.Result int result) {
+ completeMessageTransaction(transaction, result, /* iter= */ null);
+ }
+
+ /**
+ * Completes a message transaction and removes it from the reliable message map using iter.
+ *
+ * @param transaction The transaction to complete.
+ * @param result The result code.
+ * @param iter The iterator for the reliable message map - used to remove the message directly.
+ */
+ private void completeMessageTransaction(
+ ContextHubServiceTransaction transaction,
+ @ContextHubTransaction.Result int result,
+ Iterator<Map.Entry<Integer, ContextHubServiceTransaction>> iter) {
+ transaction.onTransactionComplete(result);
+
+ if (iter == null) {
+ mReliableMessageTransactionMap.remove(transaction.getMessageSequenceNumber());
+ } else {
+ iter.remove();
+ }
+ mReliableMessageHostEndpointIdActiveSet.remove(transaction.getHostEndpointId());
+
+ Log.d(
+ TAG,
+ "Successfully completed reliable message transaction with "
+ + "message sequence number = "
+ + transaction.getMessageSequenceNumber()
+ + " and result = "
+ + result);
+ }
+
+ /**
+ * Starts a message transaction.
+ *
+ * @param transaction The transaction to start.
+ * @param now The now time.
+ */
+ private void startMessageTransaction(ContextHubServiceTransaction transaction, long now) {
+ int numCompletedStartCalls = transaction.getNumCompletedStartCalls();
+ @ContextHubTransaction.Result int result = transaction.onTransact();
+ if (result == ContextHubTransaction.RESULT_SUCCESS) {
+ Log.d(
+ TAG,
+ "Successfully "
+ + (numCompletedStartCalls == 0 ? "started" : "retried")
+ + " reliable message transaction with message sequence number = "
+ + transaction.getMessageSequenceNumber());
+ } else {
+ Log.w(
+ TAG,
+ "Could not start reliable message transaction with "
+ + "message sequence number = "
+ + transaction.getMessageSequenceNumber()
+ + ", result = "
+ + result);
+ }
+
+ transaction.setNextRetryTime(now + RELIABLE_MESSAGE_RETRY_WAIT_TIME.toNanos());
+ if (transaction.getTimeoutTime() == Long.MAX_VALUE) { // first time starting transaction
+ transaction.setTimeoutTime(now + RELIABLE_MESSAGE_TIMEOUT.toNanos());
+ }
+ transaction.setNumCompletedStartCalls(numCompletedStartCalls + 1);
+ mReliableMessageHostEndpointIdActiveSet.add(transaction.getHostEndpointId());
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ int i = 0;
+ synchronized (this) {
+ for (ContextHubServiceTransaction transaction : mTransactionQueue) {
+ sb.append(i);
+ sb.append(": ");
+ sb.append(transaction.toString());
+ sb.append("\n");
+ ++i;
+ }
+
+ if (Flags.reliableMessageRetrySupportService()) {
+ for (ContextHubServiceTransaction transaction :
+ mReliableMessageTransactionMap.values()) {
+ sb.append(i);
+ sb.append(": ");
+ sb.append(transaction.toString());
+ sb.append("\n");
+ ++i;
+ }
+ }
+
+ sb.append("Transaction History:\n");
+ Iterator<TransactionRecord> iterator = mTransactionRecordDeque.descendingIterator();
+ while (iterator.hasNext()) {
+ sb.append(iterator.next());
+ sb.append("\n");
+ }
+ }
+ return sb.toString();
+ }
+}