Add additional conversion to ST for testing

Testing ST requires converting AIDL types back to API to pass
to the test client.

Add equals to RecognitionConfig.

Bug: 271330425
Test: atest ConversionUtilTest
Change-Id: I98336ac664a1a264835110a34d20b73f430d5de8
diff --git a/core/java/android/hardware/soundtrigger/ConversionUtil.java b/core/java/android/hardware/soundtrigger/ConversionUtil.java
index be57bbe..58e7f01 100644
--- a/core/java/android/hardware/soundtrigger/ConversionUtil.java
+++ b/core/java/android/hardware/soundtrigger/ConversionUtil.java
@@ -34,10 +34,12 @@
 import android.media.soundtrigger.SoundModel;
 import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
 import android.os.ParcelFileDescriptor;
+import android.system.ErrnoException;
 import android.os.SharedMemory;
 
 import java.nio.ByteBuffer;
 import java.util.Arrays;
+import java.util.Locale;
 import java.util.UUID;
 
 /** @hide */
@@ -140,6 +142,14 @@
         return aidlPhrase;
     }
 
+    public static SoundTrigger.Keyphrase aidl2apiPhrase(Phrase aidlPhrase) {
+        return new SoundTrigger.Keyphrase(aidlPhrase.id,
+                aidl2apiRecognitionModes(aidlPhrase.recognitionModes),
+                new Locale.Builder().setLanguageTag(aidlPhrase.locale).build(),
+                aidlPhrase.text,
+                Arrays.copyOf(aidlPhrase.users, aidlPhrase.users.length));
+    }
+
     public static RecognitionConfig api2aidlRecognitionConfig(
             SoundTrigger.RecognitionConfig apiConfig) {
         RecognitionConfig aidlConfig = new RecognitionConfig();
@@ -156,6 +166,21 @@
         return aidlConfig;
     }
 
+    public static SoundTrigger.RecognitionConfig aidl2apiRecognitionConfig(
+            RecognitionConfig aidlConfig) {
+        var keyphrases =
+            new SoundTrigger.KeyphraseRecognitionExtra[aidlConfig.phraseRecognitionExtras.length];
+        int i = 0;
+        for (var extras : aidlConfig.phraseRecognitionExtras) {
+            keyphrases[i++] = aidl2apiPhraseRecognitionExtra(extras);
+        }
+        return new SoundTrigger.RecognitionConfig(aidlConfig.captureRequested,
+                false /** allowMultipleTriggers **/,
+                keyphrases,
+                Arrays.copyOf(aidlConfig.data, aidlConfig.data.length),
+                aidl2apiAudioCapabilities(aidlConfig.audioCapabilities));
+    }
+
     public static PhraseRecognitionExtra api2aidlPhraseRecognitionExtra(
             SoundTrigger.KeyphraseRecognitionExtra apiExtra) {
         PhraseRecognitionExtra aidlExtra = new PhraseRecognitionExtra();
@@ -281,7 +306,7 @@
         return result;
     }
 
-    private static @Nullable ParcelFileDescriptor byteArrayToSharedMemory(byte[] data, String name) {
+    public static @Nullable ParcelFileDescriptor byteArrayToSharedMemory(byte[] data, String name) {
         if (data.length == 0) {
             return null;
         }
@@ -298,4 +323,19 @@
             throw new RuntimeException(e);
         }
     }
+
+    public static byte[] sharedMemoryToByteArray(@Nullable ParcelFileDescriptor pfd, int size) {
+        if (pfd == null || size == 0) {
+            return new byte[0];
+        }
+        try (SharedMemory mem = SharedMemory.fromFileDescriptor(pfd)) {
+            ByteBuffer buffer = mem.mapReadOnly();
+            byte[] data = new byte[(size > mem.getSize()) ? mem.getSize() : size];
+            buffer.get(data);
+            mem.unmap(buffer);
+            return data;
+        } catch (ErrnoException e) {
+            throw new RuntimeException(e);
+        }
+    }
 }
diff --git a/core/java/android/hardware/soundtrigger/SoundTrigger.java b/core/java/android/hardware/soundtrigger/SoundTrigger.java
index b7a694c..e63f57b 100644
--- a/core/java/android/hardware/soundtrigger/SoundTrigger.java
+++ b/core/java/android/hardware/soundtrigger/SoundTrigger.java
@@ -1493,6 +1493,45 @@
                     + Arrays.toString(keyphrases) + ", data=" + Arrays.toString(data)
                     + ", audioCapabilities=" + Integer.toHexString(audioCapabilities) + "]";
         }
+
+        @Override
+        public final boolean equals(Object obj) {
+            if (this == obj)
+                return true;
+            if (obj == null)
+                return false;
+            if (!(obj instanceof RecognitionConfig))
+                return false;
+            RecognitionConfig other = (RecognitionConfig) obj;
+            if (captureRequested != other.captureRequested) {
+                return false;
+            }
+            if (allowMultipleTriggers != other.allowMultipleTriggers) {
+                return false;
+            }
+            if (!Arrays.equals(keyphrases, other.keyphrases)) {
+                return false;
+            }
+            if (!Arrays.equals(data, other.data)) {
+                return false;
+            }
+            if (audioCapabilities != other.audioCapabilities) {
+                return false;
+            }
+            return true;
+        }
+
+        @Override
+        public final int hashCode() {
+            final int prime = 31;
+            int result = 1;
+            result = prime * result + (captureRequested ? 1 : 0);
+            result = prime * result + (allowMultipleTriggers ? 1 : 0);
+            result = prime * result + Arrays.hashCode(keyphrases);
+            result = prime * result + Arrays.hashCode(data);
+            result = prime * result + audioCapabilities;
+            return result;
+        }
     }
 
     /**
diff --git a/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/ConversionUtilTest.java b/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/ConversionUtilTest.java
index 0799859..5661b12 100644
--- a/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/ConversionUtilTest.java
+++ b/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/ConversionUtilTest.java
@@ -17,17 +17,32 @@
 package com.android.server.soundtrigger_middleware;
 
 import static android.hardware.soundtrigger.ConversionUtil.aidl2apiAudioFormatWithDefault;
+import static android.hardware.soundtrigger.ConversionUtil.aidl2apiPhrase;
+import static android.hardware.soundtrigger.ConversionUtil.aidl2apiRecognitionConfig;
+import static android.hardware.soundtrigger.ConversionUtil.api2aidlPhrase;
+import static android.hardware.soundtrigger.ConversionUtil.api2aidlRecognitionConfig;
+import static android.hardware.soundtrigger.ConversionUtil.byteArrayToSharedMemory;
+import static android.hardware.soundtrigger.ConversionUtil.sharedMemoryToByteArray;
+import static android.hardware.soundtrigger.SoundTrigger.ConfidenceLevel;
+import static android.hardware.soundtrigger.SoundTrigger.RECOGNITION_MODE_GENERIC;
+import static android.hardware.soundtrigger.SoundTrigger.RECOGNITION_MODE_USER_AUTHENTICATION;
+import static android.hardware.soundtrigger.SoundTrigger.RECOGNITION_MODE_USER_IDENTIFICATION;
+import static android.hardware.soundtrigger.SoundTrigger.RECOGNITION_MODE_VOICE_TRIGGER;
 
+import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 
 import android.hardware.audio.common.V2_0.Uuid;
+import android.hardware.soundtrigger.SoundTrigger;
 
 import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.Locale;
+
 @RunWith(AndroidJUnit4.class)
 public class ConversionUtilTest {
     private static final String TAG = "ConversionUtilTest";
@@ -57,4 +72,44 @@
                 );
         assertNotNull(format);
     }
+
+    @Test
+    public void testRecognitionConfigRoundTrip() {
+        final int flags = SoundTrigger.ModuleProperties.AUDIO_CAPABILITY_ECHO_CANCELLATION
+                | SoundTrigger.ModuleProperties.AUDIO_CAPABILITY_NOISE_SUPPRESSION;
+        final var data = new byte[] {0x11, 0x22};
+        final var keyphrases = new SoundTrigger.KeyphraseRecognitionExtra[2];
+        keyphrases[0] = new SoundTrigger.KeyphraseRecognitionExtra(99,
+                RECOGNITION_MODE_VOICE_TRIGGER | RECOGNITION_MODE_USER_IDENTIFICATION, 13,
+                    new ConfidenceLevel[] {new ConfidenceLevel(9999, 50),
+                                           new ConfidenceLevel(5000, 80)});
+        keyphrases[1] = new SoundTrigger.KeyphraseRecognitionExtra(101,
+                RECOGNITION_MODE_GENERIC, 8, new ConfidenceLevel[] {
+                    new ConfidenceLevel(7777, 30),
+                    new ConfidenceLevel(2222, 60)});
+
+        var apiconfig = new SoundTrigger.RecognitionConfig(true, false /** must be false **/,
+                keyphrases, data, flags);
+        assertEquals(apiconfig, aidl2apiRecognitionConfig(api2aidlRecognitionConfig(apiconfig)));
+    }
+
+    @Test
+    public void testByteArraySharedMemRoundTrip() {
+        final var data = new byte[] { 0x11, 0x22, 0x33, 0x44,
+                (byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte) 0xef };
+        assertArrayEquals(data, sharedMemoryToByteArray(byteArrayToSharedMemory(data, "name"),
+                    10000000));
+
+    }
+
+    @Test
+    public void testPhraseRoundTrip() {
+        final var users = new int[] {10001, 10002};
+        final var apiphrase = new SoundTrigger.Keyphrase(17 /** id **/,
+                RECOGNITION_MODE_VOICE_TRIGGER | RECOGNITION_MODE_USER_AUTHENTICATION,
+                Locale.forLanguageTag("no_NO"),
+                "Hello Android", /** keyphrase **/
+                users);
+        assertEquals(apiphrase, aidl2apiPhrase(api2aidlPhrase(apiphrase)));
+    }
 }