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