Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b32371dd authored by Atneya Nair's avatar Atneya Nair
Browse files

Add VIMService model enrollment override

In order to test voiceinteraction, we must be able to override the
persistent model enrollment database with an in-memory equivalent.
- Abstract DatabaseHelper into IEnrolledModelDb interface
- Implement the Test version of this interface as a hashmap
- Add VIMService APIs and client-side equivalent to override the Db with
the test version
- Remove incorrect GuardedBy annotations (no GuardedBy(this) on a
  public interface boundary)

Test: atest
AlwaysOnHotwordDetectorTest#testAlwaysOnHotwordDetector_startRecognitionWithData
Fixes: 273309878

Change-Id: Icf4f1aaf147519ef45534ee8c7ae26c6040fa4cd
parent 31b112fe
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -2093,6 +2093,14 @@ package android.media.tv.tuner {

}

package android.media.voice {

  public final class KeyphraseModelManager {
    method @RequiresPermission("android.permission.MANAGE_VOICE_KEYPHRASES") public void setModelDatabaseForTestEnabled(boolean);
  }

}

package android.net {

  public class NetworkPolicyManager {
+16 −0
Original line number Diff line number Diff line
@@ -96,6 +96,21 @@ interface IVoiceInteractionManagerService {
     * @RequiresPermission Manifest.permission.MANAGE_VOICE_KEYPHRASES
     */
    int deleteKeyphraseSoundModel(int keyphraseId, in String bcp47Locale);

    /**
     * Override the persistent enrolled model database with an in-memory
     * fake for testing purposes.
     *
     * @param enabled - {@code true} to enable the test database. {@code false} to enable
     * the real, persistent database.
     * @param token - IBinder used to register a death listener to clean-up the override
     * if tests do not clean up gracefully.
     */
    @EnforcePermission("MANAGE_VOICE_KEYPHRASES")
    @JavaPassthrough(annotation= "@android.annotation.RequiresPermission(" +
            "android.Manifest.permission.MANAGE_VOICE_KEYPHRASES)")
    void setModelDatabaseForTestEnabled(boolean enabled, IBinder token);

    /**
     * Indicates if there's a keyphrase sound model available for the given keyphrase ID and the
     * user ID of the caller.
@@ -106,6 +121,7 @@ interface IVoiceInteractionManagerService {
     * @param bcp47Locale The BCP47 language tag  for the keyphrase's locale.
     */
    boolean isEnrolledForKeyphrase(int keyphraseId, String bcp47Locale);

    /**
     * Generates KeyphraseMetadata for an enrolled sound model based on keyphrase string, locale,
     * and the user ID of the caller.
+21 −0
Original line number Diff line number Diff line
@@ -21,7 +21,9 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.hardware.soundtrigger.SoundTrigger;
import android.os.Binder;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.util.Slog;
@@ -154,4 +156,23 @@ public final class KeyphraseModelManager {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
     * Override the persistent enrolled model database with an in-memory
     * fake for testing purposes.
     *
     * @param enabled - {@code true} if the model enrollment database should be overridden with an
     * in-memory fake. {@code false} if the real, persistent model enrollment database should be
     * used.
     * @hide
     */
    @RequiresPermission(Manifest.permission.MANAGE_VOICE_KEYPHRASES)
    @TestApi
    public void setModelDatabaseForTestEnabled(boolean enabled) {
        try {
            mVoiceInteractionManagerService.setModelDatabaseForTestEnabled(enabled, new Binder());
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }
}
+5 −21
Original line number Diff line number Diff line
@@ -39,7 +39,7 @@ import java.util.UUID;
 *
 * @hide
 */
public class DatabaseHelper extends SQLiteOpenHelper {
public class DatabaseHelper extends SQLiteOpenHelper implements IEnrolledModelDb {
    static final String TAG = "SoundModelDBHelper";
    static final boolean DBG = false;

@@ -153,11 +153,7 @@ public class DatabaseHelper extends SQLiteOpenHelper {
        }
    }

    /**
     * Updates the given keyphrase model, adds it, if it doesn't already exist.
     *
     * TODO: We only support one keyphrase currently.
     */
    @Override
    public boolean updateKeyphraseSoundModel(KeyphraseSoundModel soundModel) {
        synchronized(this) {
            SQLiteDatabase db = getWritableDatabase();
@@ -193,9 +189,7 @@ public class DatabaseHelper extends SQLiteOpenHelper {
        }
    }

    /**
     * Deletes the sound model and associated keyphrases.
     */
    @Override
    public boolean deleteKeyphraseSoundModel(int keyphraseId, int userHandle, String bcp47Locale) {
        // Normalize the locale to guard against SQL injection.
        bcp47Locale = Locale.forLanguageTag(bcp47Locale).toLanguageTag();
@@ -218,12 +212,7 @@ public class DatabaseHelper extends SQLiteOpenHelper {
        }
    }

    /**
     * Returns a matching {@link KeyphraseSoundModel} for the keyphrase ID.
     * Returns null if a match isn't found.
     *
     * TODO: We only support one keyphrase currently.
     */
    @Override
    public KeyphraseSoundModel getKeyphraseSoundModel(int keyphraseId, int userHandle,
            String bcp47Locale) {
        // Sanitize the locale to guard against SQL injection.
@@ -237,12 +226,7 @@ public class DatabaseHelper extends SQLiteOpenHelper {
        }
    }

    /**
     * Returns a matching {@link KeyphraseSoundModel} for the keyphrase string.
     * Returns null if a match isn't found.
     *
     * TODO: We only support one keyphrase currently.
     */
    @Override
    public KeyphraseSoundModel getKeyphraseSoundModel(String keyphrase, int userHandle,
            String bcp47Locale) {
        // Sanitize the locale to guard against SQL injection.
+90 −0
Original line number Diff line number Diff line
/**
 * Copyright (C) 2023 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.voiceinteraction;

import android.hardware.soundtrigger.SoundTrigger.Keyphrase;
import android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel;

import java.io.PrintWriter;

/**
 * Interface for registering and querying the enrolled keyphrase model database for
 * {@link VoiceInteractionManagerService}.
 * This interface only supports one keyphrase per {@link KeyphraseSoundModel}.
 * The non-update methods are uniquely keyed on fields of the first keyphrase
 * {@link KeyphraseSoundModel#getKeyphrases()}.
 * @hide
 */
public interface IEnrolledModelDb {

    //TODO(273286174): We only support one keyphrase currently.
    /**
     * Register the given {@link KeyphraseSoundModel}, or updates it if it already exists.
     *
     * @param soundModel - The sound model to register in the database.
     * Updates the sound model if the keyphrase id, users, locale match an existing entry.
     * Must have one and only one associated {@link Keyphrase}.
     * @return - {@code true} if successful, {@code false} if unsuccessful
     */
    boolean updateKeyphraseSoundModel(KeyphraseSoundModel soundModel);

    /**
     * Deletes the previously registered keyphrase sound model from the database.
     *
     * @param keyphraseId - The (first) keyphrase ID of the KeyphraseSoundModel to delete.
     * @param userHandle - The user handle making this request. Must be included in the user
     *                     list of the registered sound model.
     * @param bcp47Locale - The locale of the (first) keyphrase associated with this model.
     * @return - {@code true} if successful, {@code false} if unsuccessful
     */
    boolean deleteKeyphraseSoundModel(int keyphraseId, int userHandle, String bcp47Locale);

    //TODO(273286174): We only support one keyphrase currently.
    /**
     * Returns the first matching {@link KeyphraseSoundModel} for the keyphrase ID, locale pair,
     * contingent on the userHandle existing in the user list for the model.
     * Returns null if a match isn't found.
     *
     * @param keyphraseId - The (first) keyphrase ID of the KeyphraseSoundModel to query.
     * @param userHandle - The user handle making this request. Must be included in the user
     *                     list of the registered sound model.
     * @param bcp47Locale - The locale of the (first) keyphrase associated with this model.
     * @return - {@code true} if successful, {@code false} if unsuccessful
     */
    KeyphraseSoundModel getKeyphraseSoundModel(int keyphraseId, int userHandle,
            String bcp47Locale);

    //TODO(273286174): We only support one keyphrase currently.
    /**
     * Returns the first matching {@link KeyphraseSoundModel} for the keyphrase ID, locale pair,
     * contingent on the userHandle existing in the user list for the model.
     * Returns null if a match isn't found.
     *
     * @param keyphrase - The text of (the first) keyphrase of the KeyphraseSoundModel to query.
     * @param userHandle - The user handle making this request. Must be included in the user
     *                     list of the registered sound model.
     * @param bcp47Locale - The locale of the (first) keyphrase associated with this model.
     * @return - {@code true} if successful, {@code false} if unsuccessful
     */
    KeyphraseSoundModel getKeyphraseSoundModel(String keyphrase, int userHandle,
            String bcp47Locale);

    /**
     * Dumps contents of database for dumpsys
     */
    void dump(PrintWriter pw);
}
Loading