Add start/stop detection functionality for VQDS

This CL enables the VQDS to start and stop detection from VQD. It also
defines intermediate callbacks to send query related signals and data
to the interactor.

Bug: 261783819
Test: Manual
Change-Id: I8c7f99a8173710680c8c506405d9de6de1fc37d9
diff --git a/core/java/android/service/voice/VisualQueryDetector.java b/core/java/android/service/voice/VisualQueryDetector.java
index d24e69d..6917576 100644
--- a/core/java/android/service/voice/VisualQueryDetector.java
+++ b/core/java/android/service/voice/VisualQueryDetector.java
@@ -64,7 +64,6 @@
             IVoiceInteractionManagerService managerService,
             @NonNull @CallbackExecutor Executor executor,
             Callback callback) {
-
         mManagerService = managerService;
         mCallback = callback;
         mExecutor = executor;
@@ -109,8 +108,18 @@
         if (DEBUG) {
             Slog.i(TAG, "#startRecognition");
         }
-        // TODO(b/261783819): Call StartDetection on VisualQueryDetectionService with the system.
-        return false;
+        // check if the detector is active with the initialization delegate
+        mInitializationDelegate.startRecognition();
+
+        try {
+            mManagerService.startPerceiving(new BinderCallback(mExecutor, mCallback));
+        } catch (SecurityException e) {
+            Slog.e(TAG, "startRecognition failed: " + e);
+            return false;
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+        return true;
     }
 
     /**
@@ -123,8 +132,15 @@
         if (DEBUG) {
             Slog.i(TAG, "#stopRecognition");
         }
-        // TODO(b/261783819): Call StopDetection on VisualQueryDetectionService with the system.
-        return false;
+        // check if the detector is active with the initialization delegate
+        mInitializationDelegate.startRecognition();
+
+        try {
+            mManagerService.stopPerceiving();
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+        return true;
     }
 
     /**
@@ -219,14 +235,14 @@
 
         @Override
         public boolean stopRecognition() throws IllegalDetectorStateException {
-            //No-op, we only reuse the initialization methods.
-            return false;
+            throwIfDetectorIsNoLongerActive();
+            return true;
         }
 
         @Override
         public boolean startRecognition() throws IllegalDetectorStateException {
-            //No-op, we only reuse the initialization methods.
-            return false;
+            throwIfDetectorIsNoLongerActive();
+            return true;
         }
 
         @Override
@@ -244,6 +260,49 @@
         }
     }
 
+    private static class BinderCallback
+            extends IVisualQueryDetectionVoiceInteractionCallback.Stub {
+        private final Executor mExecutor;
+        private final VisualQueryDetector.Callback mCallback;
+
+        BinderCallback(Executor executor, VisualQueryDetector.Callback callback) {
+            this.mExecutor = executor;
+            this.mCallback = callback;
+        }
+
+        /** Called when the detected result is valid. */
+        @Override
+        public void onQueryDetected(@NonNull String partialQuery) {
+            Slog.v(TAG, "BinderCallback#onQueryDetected");
+            Binder.withCleanCallingIdentity(() -> mExecutor.execute(
+                    () -> mCallback.onQueryDetected(partialQuery)));
+        }
+
+        @Override
+        public void onQueryFinished() {
+            Slog.v(TAG, "BinderCallback#onQueryFinished");
+            Binder.withCleanCallingIdentity(() -> mExecutor.execute(
+                    () -> mCallback.onQueryFinished()));
+        }
+
+        @Override
+        public void onQueryRejected() {
+            Slog.v(TAG, "BinderCallback#onQueryRejected");
+            Binder.withCleanCallingIdentity(() -> mExecutor.execute(
+                    () -> mCallback.onQueryRejected()));
+        }
+
+        /** Called when the detection fails due to an error. */
+        @Override
+        public void onError() {
+            Slog.v(TAG, "BinderCallback#onError");
+            Binder.withCleanCallingIdentity(() -> mExecutor.execute(
+                    () -> mCallback.onError()));
+        }
+
+    }
+
+
     private static class InitializationStateListener
             extends IHotwordRecognitionStatusCallback.Stub {
         private final Executor mExecutor;