| /* |
| * 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; |
| } |
| } |