VoiceInteraction: Fix testVoiceInteractorDestroy flakiness.

The test activity checks that the interactor is properly marked destroyed in
the callback that executes when the interactor is destroyed. There is a race
condition because there's no synchronization between
VoiceInteractor.destroy() and the accessors on VoiceInteractor/Activity.

With this change, the accessors are polled until the values update.

Also moves these checks out of the onDestroyedCallback; there's no reason to
keep them there and it's a bit more readable with them moved out.

Bug: 178325554
Test: atest CtsVoiceInteractionTestCases --iterations
Change-Id: I000ace2f207473eafad3e2ca48a6b279883a6d0a
diff --git a/tests/tests/voiceinteraction/testapp/Android.bp b/tests/tests/voiceinteraction/testapp/Android.bp
index 94e1a0e..a01e43f 100644
--- a/tests/tests/voiceinteraction/testapp/Android.bp
+++ b/tests/tests/voiceinteraction/testapp/Android.bp
@@ -15,7 +15,11 @@
 android_test_helper_app {
     name: "CtsVoiceInteractionApp",
     defaults: ["cts_support_defaults"],
-    static_libs: ["CtsVoiceInteractionCommon", "androidx.core_core"],
+    static_libs: [
+        "CtsVoiceInteractionCommon",
+        "androidx.core_core",
+        "compatibility-device-util-axt",
+    ],
     srcs: ["src/**/*.java"],
     sdk_version: "test_current",
     // Tag this module as a cts test artifact
diff --git a/tests/tests/voiceinteraction/testapp/src/android/voiceinteraction/testapp/DirectActionsActivity.java b/tests/tests/voiceinteraction/testapp/src/android/voiceinteraction/testapp/DirectActionsActivity.java
index b64c734..5f36d8960 100644
--- a/tests/tests/voiceinteraction/testapp/src/android/voiceinteraction/testapp/DirectActionsActivity.java
+++ b/tests/tests/voiceinteraction/testapp/src/android/voiceinteraction/testapp/DirectActionsActivity.java
@@ -29,6 +29,10 @@
 
 import androidx.annotation.NonNull;
 
+import com.android.compatibility.common.util.PollingCheck;
+
+import com.google.common.truth.Truth;
+
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -118,19 +122,29 @@
     }
 
     private void detectDestroyedInteractor(@NonNull RemoteCallback callback) {
-        final Bundle result = new Bundle();
         final CountDownLatch latch = new CountDownLatch(1);
-
         final VoiceInteractor interactor = getVoiceInteractor();
-        interactor.registerOnDestroyedCallback(AsyncTask.THREAD_POOL_EXECUTOR, () -> {
-            if (interactor.isDestroyed() && getVoiceInteractor() == null) {
-                result.putBoolean(Utils.DIRECT_ACTIONS_KEY_RESULT, true);
-            }
-            latch.countDown();
-        });
-
+        interactor.registerOnDestroyedCallback(AsyncTask.THREAD_POOL_EXECUTOR, latch::countDown);
         Utils.await(latch);
 
+        try {
+            // Check that the interactor is properly marked destroyed. Polls the values since
+            // there's no synchronization between destroy() and these methods.
+            long pollingTimeoutMs = 3000;
+            PollingCheck.check(
+                    "onDestroyedCallback called but interactor isn't destroyed",
+                    pollingTimeoutMs,
+                    interactor::isDestroyed);
+            PollingCheck.check(
+                    "onDestroyedCallback called but activity still has an interactor",
+                    pollingTimeoutMs,
+                    () -> getVoiceInteractor() == null);
+        } catch (Exception e) {
+            Truth.assertWithMessage("Unexpected exception: " + e).fail();
+        }
+
+        final Bundle result = new Bundle();
+        result.putBoolean(Utils.DIRECT_ACTIONS_KEY_RESULT, true);
         Log.v(TAG, "detectDestroyedInteractor(): " + Utils.toBundleString(result));
         callback.sendResult(result);
     }