pw_transfer: Don't call Finish() on inactive transfers in the C++ client

This adds the same bugfix as pwrev/75980 to the transfer client.

These bugs are indicative of a design issue in the transfer client;
inactive transfers contexts should not be invoked. This will be fixed
in a later change -- a tracking bug is created.

Change-Id: I0ffc45d3c9343587fd4574ce13cd8e2762b612ea
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/77200
Pigweed-Auto-Submit: Alexei Frolov <frolv@google.com>
Reviewed-by: Wyatt Hepler <hepler@google.com>
Commit-Queue: Auto-Submit <auto-submit@pigweed.google.com.iam.gserviceaccount.com>
diff --git a/pw_transfer/client.cc b/pw_transfer/client.cc
index 80d060c..1a6ffb1 100644
--- a/pw_transfer/client.cc
+++ b/pw_transfer/client.cc
@@ -110,7 +110,10 @@
   return context->InitiateTransfer(max_parameters_);
 }
 
-Client::ClientContext* Client::GetActiveTransfer(uint32_t transfer_id) {
+// TODO(pwbug/592): This function should be updated to only return active
+// transfers. Calling ReadChunkData() / Finish() on inactive transfers is
+// unintuitive and has led to several bugs where not all cases are handled.
+Client::ClientContext* Client::GetTransferById(uint32_t transfer_id) {
   std::lock_guard lock(transfer_context_mutex_);
   auto it =
       std::find_if(transfer_contexts_.begin(),
@@ -133,7 +136,7 @@
     return;
   }
 
-  ClientContext* ctx = GetActiveTransfer(chunk.transfer_id);
+  ClientContext* ctx = GetTransferById(chunk.transfer_id);
   if (ctx == nullptr) {
     // TODO(frolv): Handle this error case.
     return;
@@ -143,7 +146,10 @@
     PW_LOG_ERROR(
         "Received a read chunk for transfer %u, but it is a write transfer",
         static_cast<unsigned>(ctx->transfer_id()));
-    ctx->Finish(Status::Internal());
+    if (ctx->active()) {
+      // TODO(pwbug/592): Remove the active() check.
+      ctx->Finish(Status::Internal());
+    }
     return;
   }
 
@@ -151,14 +157,20 @@
     PW_LOG_ERROR(
         "Received a write chunk for transfer %u, but it is a read transfer",
         static_cast<unsigned>(ctx->transfer_id()));
-    ctx->Finish(Status::Internal());
+    if (ctx->active()) {
+      // TODO(pwbug/592): Remove the active() check.
+      ctx->Finish(Status::Internal());
+    }
     return;
   }
 
-  if (chunk.status.has_value()) {
+  if (chunk.status.has_value() && ctx->active()) {
     // A status field indicates that the transfer has finished.
+    //
     // TODO(frolv): This is invoked from the RPC client thread -- should it be
     // run in the work queue instead?
+    //
+    // TODO(pwbug/592): Remove the active() check.
     ctx->Finish(chunk.status.value());
     return;
   }
diff --git a/pw_transfer/public/pw_transfer/client.h b/pw_transfer/public/pw_transfer/client.h
index 4501103..12f43a8 100644
--- a/pw_transfer/public/pw_transfer/client.h
+++ b/pw_transfer/public/pw_transfer/client.h
@@ -97,7 +97,7 @@
                           CompletionFunc&& on_completion,
                           chrono::SystemClock::duration timeout);
 
-  ClientContext* GetActiveTransfer(uint32_t transfer_id);
+  ClientContext* GetTransferById(uint32_t transfer_id);
 
   // Function called when a chunk is received, from the context of the RPC
   // client thread.