/* * Copyright (C) 2014 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.test.voiceenrollment; import android.annotation.Nullable; import android.content.Context; import android.hardware.soundtrigger.KeyphraseEnrollmentInfo; import android.hardware.soundtrigger.SoundTrigger; import android.hardware.soundtrigger.SoundTrigger.Keyphrase; import android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel; import android.os.RemoteException; import android.os.ServiceManager; import android.service.voice.AlwaysOnHotwordDetector; import android.util.Log; import com.android.internal.app.IVoiceInteractionManagerService; /** * Utility class for the enrollment operations like enroll;re-enroll & un-enroll. */ public class EnrollmentUtil { private static final String TAG = "TestEnrollmentUtil"; /** * Activity Action: Show activity for managing the keyphrases for hotword detection. * This needs to be defined by an activity that supports enrolling users for hotword/keyphrase * detection. */ public static final String ACTION_MANAGE_VOICE_KEYPHRASES = KeyphraseEnrollmentInfo.ACTION_MANAGE_VOICE_KEYPHRASES; /** * Intent extra: The intent extra for the specific manage action that needs to be performed. * Possible values are {@link AlwaysOnHotwordDetector#MANAGE_ACTION_ENROLL}, * {@link AlwaysOnHotwordDetector#MANAGE_ACTION_RE_ENROLL} * or {@link AlwaysOnHotwordDetector#MANAGE_ACTION_UN_ENROLL}. */ public static final String EXTRA_VOICE_KEYPHRASE_ACTION = KeyphraseEnrollmentInfo.EXTRA_VOICE_KEYPHRASE_ACTION; /** * Intent extra: The hint text to be shown on the voice keyphrase management UI. */ public static final String EXTRA_VOICE_KEYPHRASE_HINT_TEXT = KeyphraseEnrollmentInfo.EXTRA_VOICE_KEYPHRASE_HINT_TEXT; /** * Intent extra: The voice locale to use while managing the keyphrase. */ public static final String EXTRA_VOICE_KEYPHRASE_LOCALE = KeyphraseEnrollmentInfo.EXTRA_VOICE_KEYPHRASE_LOCALE; /** Simple recognition of the key phrase */ public static final int RECOGNITION_MODE_VOICE_TRIGGER = SoundTrigger.RECOGNITION_MODE_VOICE_TRIGGER; /** Trigger only if one user is identified */ public static final int RECOGNITION_MODE_USER_IDENTIFICATION = SoundTrigger.RECOGNITION_MODE_USER_IDENTIFICATION; private final IVoiceInteractionManagerService mModelManagementService; public EnrollmentUtil() { mModelManagementService = IVoiceInteractionManagerService.Stub.asInterface( ServiceManager.getService(Context.VOICE_INTERACTION_MANAGER_SERVICE)); } /** * Adds/Updates a sound model. * The sound model must contain a valid UUID, * exactly 1 keyphrase, * and users for which the keyphrase is valid - typically the current user. * * @param soundModel The sound model to add/update. * @return {@code true} if the call succeeds, {@code false} otherwise. */ public boolean addOrUpdateSoundModel(KeyphraseSoundModel soundModel) { if (!verifyKeyphraseSoundModel(soundModel)) { return false; } int status = SoundTrigger.STATUS_ERROR; try { status = mModelManagementService.updateKeyphraseSoundModel(soundModel); } catch (RemoteException e) { Log.e(TAG, "RemoteException in updateKeyphraseSoundModel", e); } return status == SoundTrigger.STATUS_OK; } /** * Gets the sound model for the given keyphrase, null if none exists. * This should be used for re-enrollment purposes. * If a sound model for a given keyphrase exists, and it needs to be updated, * it should be obtained using this method, updated and then passed in to * {@link #addOrUpdateSoundModel(KeyphraseSoundModel)} without changing the IDs. * * @param keyphraseId The keyphrase ID to look-up the sound model for. * @param bcp47Locale The locale for with to look up the sound model for. * @return The sound model if one was found, null otherwise. */ @Nullable public KeyphraseSoundModel getSoundModel(int keyphraseId, String bcp47Locale) { if (keyphraseId <= 0) { Log.e(TAG, "Keyphrase must have a valid ID"); return null; } KeyphraseSoundModel model = null; try { model = mModelManagementService.getKeyphraseSoundModel(keyphraseId, bcp47Locale); } catch (RemoteException e) { Log.e(TAG, "RemoteException in updateKeyphraseSoundModel"); } if (model == null) { Log.w(TAG, "No models present for the gien keyphrase ID"); return null; } else { return model; } } /** * Deletes the sound model for the given keyphrase id. * * @param keyphraseId The keyphrase ID to look-up the sound model for. * @return {@code true} if the call succeeds, {@code false} otherwise. */ public boolean deleteSoundModel(int keyphraseId, String bcp47Locale) { if (keyphraseId <= 0) { Log.e(TAG, "Keyphrase must have a valid ID"); return false; } int status = SoundTrigger.STATUS_ERROR; try { status = mModelManagementService.deleteKeyphraseSoundModel(keyphraseId, bcp47Locale); } catch (RemoteException e) { Log.e(TAG, "RemoteException in updateKeyphraseSoundModel"); } return status == SoundTrigger.STATUS_OK; } private boolean verifyKeyphraseSoundModel(KeyphraseSoundModel soundModel) { if (soundModel == null) { Log.e(TAG, "KeyphraseSoundModel must be non-null"); return false; } if (soundModel.uuid == null) { Log.e(TAG, "KeyphraseSoundModel must have a UUID"); return false; } if (soundModel.data == null) { Log.e(TAG, "KeyphraseSoundModel must have data"); return false; } if (soundModel.keyphrases == null || soundModel.keyphrases.length != 1) { Log.e(TAG, "Keyphrase must be exactly 1"); return false; } Keyphrase keyphrase = soundModel.keyphrases[0]; if (keyphrase.id <= 0) { Log.e(TAG, "Keyphrase must have a valid ID"); return false; } if (keyphrase.recognitionModes < 0) { Log.e(TAG, "Recognition modes must be valid"); return false; } if (keyphrase.locale == null) { Log.e(TAG, "Locale must not be null"); return false; } if (keyphrase.text == null) { Log.e(TAG, "Text must not be null"); return false; } if (keyphrase.users == null || keyphrase.users.length == 0) { Log.e(TAG, "Keyphrase must have valid user(s)"); return false; } return true; } }