Move sound trigger calls to VoiceInteractionManagerService

- This ensures that any data being loaded on the DSP comes from the framework

Change-Id: Ie15f0994850ba8f298ca07c49fe0b89e066d9e2b
This commit is contained in:
Sandeep Siddhartha
2014-07-17 16:21:54 -07:00
parent 6eb262c351
commit 055897208d
16 changed files with 605 additions and 406 deletions

View File

@@ -167,6 +167,7 @@ LOCAL_SRC_FILES += \
core/java/android/hardware/location/IGeofenceHardware.aidl \
core/java/android/hardware/location/IGeofenceHardwareCallback.aidl \
core/java/android/hardware/location/IGeofenceHardwareMonitorCallback.aidl \
core/java/android/hardware/soundtrigger/IRecognitionStatusCallback.aidl \
core/java/android/hardware/usb/IUsbManager.aidl \
core/java/android/net/IConnectivityManager.aidl \
core/java/android/net/IEthernetManager.aidl \

View File

@@ -27242,7 +27242,7 @@ package android.service.voice {
field public static final int RECOGNITION_STATUS_NOT_REQUESTED = 2; // 0x2
field public static final int RECOGNITION_STATUS_REQUESTED = 4; // 0x4
field public static final int STATUS_ERROR = -2147483648; // 0x80000000
field public static final int STATUS_OK = 1; // 0x1
field public static final int STATUS_OK = 0; // 0x0
}
public static abstract interface AlwaysOnHotwordDetector.Callback {

View File

@@ -1,57 +0,0 @@
/**
* 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 android.hardware.soundtrigger;
import java.util.UUID;
/**
* Properties of the DSP hardware on the device.
*
* @hide
*/
public class DspInfo {
/**
* Unique voice engine Id (changes with each version).
*/
public final UUID voiceEngineId;
/**
* Human readable voice detection engine implementor.
*/
public final String voiceEngineImplementor;
/**
* Human readable voice detection engine description.
*/
public final String voiceEngineDescription;
/**
* Human readable voice detection engine version
*/
public final int voiceEngineVersion;
/**
* Rated power consumption when detection is active.
*/
public final int powerConsumptionMw;
public DspInfo(UUID voiceEngineId, String voiceEngineImplementor,
String voiceEngineDescription, int version, int powerConsumptionMw) {
this.voiceEngineId = voiceEngineId;
this.voiceEngineImplementor = voiceEngineImplementor;
this.voiceEngineDescription = voiceEngineDescription;
this.voiceEngineVersion = version;
this.powerConsumptionMw = powerConsumptionMw;
}
}

View File

@@ -0,0 +1,39 @@
/*
* 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 android.hardware.soundtrigger;
/**
* @hide
*/
oneway interface IRecognitionStatusCallback {
/**
* Called when the keyphrase is spoken.
*
* @param data Optional trigger audio data, if it was requested and is available.
* TODO: See if the data being passed in works well, if not use shared memory.
* This *MUST* not exceed 100K.
*/
void onDetected(in byte[] data);
/**
* Called when the detection for the associated keyphrase starts.
*/
void onDetectionStarted();
/**
* Called when the detection for the associated keyphrase stops.
*/
void onDetectionStopped();
}

View File

@@ -1,4 +0,0 @@
package android.hardware.soundtrigger;
// @hide
parcelable Keyphrase;

View File

@@ -1,135 +0,0 @@
/**
* 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 android.hardware.soundtrigger;
import android.os.Parcel;
import android.os.Parcelable;
/**
* A Voice Keyphrase.
*
* @hide
*/
public class Keyphrase implements Parcelable {
/** A unique identifier for this keyphrase */
public final int id;
/** A hint text to display corresponding to this keyphrase, e.g. "Hello There". */
public final String hintText;
/** The locale of interest when using this Keyphrase. */
public final String locale;
/** The various recognition modes supported by this keyphrase */
public final int recognitionModeFlags;
/** The users associated with this keyphrase */
public final int[] users;
public static final Parcelable.Creator<Keyphrase> CREATOR
= new Parcelable.Creator<Keyphrase>() {
public Keyphrase createFromParcel(Parcel in) {
return Keyphrase.fromParcel(in);
}
public Keyphrase[] newArray(int size) {
return new Keyphrase[size];
}
};
private static Keyphrase fromParcel(Parcel in) {
int id = in.readInt();
String hintText = in.readString();
String locale = in.readString();
int recognitionModeFlags = in.readInt();
int numUsers = in.readInt();
int[] users = null;
if (numUsers > 0) {
users = new int[numUsers];
in.readIntArray(users);
}
return new Keyphrase(id, hintText, locale, recognitionModeFlags, users);
}
public Keyphrase(int id, String hintText, String locale, int recognitionModeFlags,
int[] users) {
this.id = id;
this.hintText = hintText;
this.locale = locale;
this.recognitionModeFlags = recognitionModeFlags;
this.users = users;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(id);
dest.writeString(hintText);
dest.writeString(locale);
dest.writeInt(recognitionModeFlags);
if (users != null) {
dest.writeInt(users.length);
dest.writeIntArray(users);
} else {
dest.writeInt(0);
}
}
@Override
public int describeContents() {
return 0;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((hintText == null) ? 0 : hintText.hashCode());
result = prime * result + id;
result = prime * result + ((locale == null) ? 0 : locale.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Keyphrase other = (Keyphrase) obj;
if (hintText == null) {
if (other.hintText != null)
return false;
} else if (!hintText.equals(other.hintText))
return false;
if (id != other.id)
return false;
if (locale == null) {
if (other.locale != null)
return false;
} else if (!locale.equals(other.locale))
return false;
return true;
}
@Override
public String toString() {
return "Keyphrase[id=" + id + ", text=" + hintText + ", locale=" + locale
+ ", recognitionModes=" + recognitionModeFlags + "]";
}
protected SoundTrigger.Keyphrase convertToSoundTriggerKeyphrase() {
return new SoundTrigger.Keyphrase(id, recognitionModeFlags, locale, hintText, users);
}
}

View File

@@ -1,4 +0,0 @@
package android.hardware.soundtrigger;
// @hide
parcelable KeyphraseSoundModel;

View File

@@ -1,79 +0,0 @@
package android.hardware.soundtrigger;
import android.os.Parcel;
import android.os.Parcelable;
import java.util.UUID;
/**
* A KeyphraseSoundModel is a sound model capable of detecting voice keyphrases.
* It contains data needed by the hardware to detect a given number of key phrases
* and the list of corresponding {@link Keyphrase}s.
*
* @hide
*/
public class KeyphraseSoundModel implements Parcelable {
/** Key phrases in this sound model */
public final Keyphrase[] keyphrases;
public final byte[] data;
public final UUID uuid;
public static final Parcelable.Creator<KeyphraseSoundModel> CREATOR
= new Parcelable.Creator<KeyphraseSoundModel>() {
public KeyphraseSoundModel createFromParcel(Parcel in) {
return KeyphraseSoundModel.fromParcel(in);
}
public KeyphraseSoundModel[] newArray(int size) {
return new KeyphraseSoundModel[size];
}
};
public KeyphraseSoundModel(UUID uuid, byte[] data,Keyphrase[] keyPhrases) {
this.uuid = uuid;
this.data = data;
this.keyphrases = keyPhrases;
}
private static KeyphraseSoundModel fromParcel(Parcel in) {
UUID uuid = UUID.fromString(in.readString());
int dataLength = in.readInt();
byte[] data = null;
if (dataLength > 0) {
data = new byte[in.readInt()];
in.readByteArray(data);
}
Keyphrase[] keyphrases =
(Keyphrase[]) in.readParcelableArray(Keyphrase.class.getClassLoader());
return new KeyphraseSoundModel(uuid, data, keyphrases);
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(uuid.toString());
if (data != null) {
dest.writeInt(data.length);
dest.writeByteArray(data);
} else {
dest.writeInt(0);
}
dest.writeParcelableArray(keyphrases, 0);
}
public SoundTrigger.KeyphraseSoundModel convertToSoundTriggerKeyphraseSoundModel() {
SoundTrigger.Keyphrase[] stKeyphrases = null;
if (keyphrases != null) {
stKeyphrases = new SoundTrigger.Keyphrase[keyphrases.length];
for (int i = 0; i < keyphrases.length; i++) {
stKeyphrases[i] = keyphrases[i].convertToSoundTriggerKeyphrase();
}
}
return new SoundTrigger.KeyphraseSoundModel(uuid, data, stKeyphrases);
}
}

View File

@@ -0,0 +1,24 @@
/**
* 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 android.hardware.soundtrigger;
parcelable SoundTrigger.ConfidenceLevel;
parcelable SoundTrigger.Keyphrase;
parcelable SoundTrigger.KeyphraseRecognitionExtra;
parcelable SoundTrigger.KeyphraseSoundModel;
parcelable SoundTrigger.ModuleProperties;
parcelable SoundTrigger.RecognitionConfig;

View File

@@ -17,8 +17,11 @@
package android.hardware.soundtrigger;
import android.os.Handler;
import android.os.Parcel;
import android.os.Parcelable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.UUID;
/**
@@ -43,7 +46,7 @@ public class SoundTrigger {
* ID used to target any API call to this paricular module. Module
* properties are returned by listModules() method.
****************************************************************************/
public static class ModuleProperties {
public static class ModuleProperties implements Parcelable {
/** Unique module ID provided by the native service */
public final int id;
@@ -102,6 +105,70 @@ public class SoundTrigger {
this.supportsConcurrentCapture = supportsConcurrentCapture;
this.powerConsumptionMw = powerConsumptionMw;
}
public static final Parcelable.Creator<ModuleProperties> CREATOR
= new Parcelable.Creator<ModuleProperties>() {
public ModuleProperties createFromParcel(Parcel in) {
return ModuleProperties.fromParcel(in);
}
public ModuleProperties[] newArray(int size) {
return new ModuleProperties[size];
}
};
private static ModuleProperties fromParcel(Parcel in) {
int id = in.readInt();
String implementor = in.readString();
String description = in.readString();
String uuid = in.readString();
int version = in.readInt();
int maxSoundModels = in.readInt();
int maxKeyphrases = in.readInt();
int maxUsers = in.readInt();
int recognitionModes = in.readInt();
boolean supportsCaptureTransition = in.readByte() == 1;
int maxBufferMs = in.readInt();
boolean supportsConcurrentCapture = in.readByte() == 1;
int powerConsumptionMw = in.readInt();
return new ModuleProperties(id, implementor, description, uuid, version,
maxSoundModels, maxKeyphrases, maxUsers, recognitionModes,
supportsCaptureTransition, maxBufferMs, supportsConcurrentCapture,
powerConsumptionMw);
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(id);
dest.writeString(implementor);
dest.writeString(description);
dest.writeString(uuid.toString());
dest.writeInt(version);
dest.writeInt(maxSoundModels);
dest.writeInt(maxKeyphrases);
dest.writeInt(maxUsers);
dest.writeInt(recognitionModes);
dest.writeByte((byte) (supportsCaptureTransition ? 1 : 0));
dest.writeInt(maxBufferMs);
dest.writeByte((byte) (supportsConcurrentCapture ? 1 : 0));
dest.writeInt(powerConsumptionMw);
}
@Override
public int describeContents() {
return 0;
}
@Override
public String toString() {
return "ModuleProperties [id=" + id + ", implementor=" + implementor + ", description="
+ description + ", uuid=" + uuid + ", version=" + version + ", maxSoundModels="
+ maxSoundModels + ", maxKeyphrases=" + maxKeyphrases + ", maxUsers="
+ maxUsers + ", recognitionModes=" + recognitionModes
+ ", supportsCaptureTransition=" + supportsCaptureTransition + ", maxBufferMs="
+ maxBufferMs + ", supportsConcurrentCapture=" + supportsConcurrentCapture
+ ", powerConsumptionMw=" + powerConsumptionMw + "]";
}
}
/*****************************************************************************
@@ -137,7 +204,7 @@ public class SoundTrigger {
* A Keyphrase describes a key phrase that can be detected by a
* {@link KeyphraseSoundModel}
****************************************************************************/
public static class Keyphrase {
public static class Keyphrase implements Parcelable {
/** Unique identifier for this keyphrase */
public final int id;
@@ -161,6 +228,96 @@ public class SoundTrigger {
this.text = text;
this.users = users;
}
public static final Parcelable.Creator<Keyphrase> CREATOR
= new Parcelable.Creator<Keyphrase>() {
public Keyphrase createFromParcel(Parcel in) {
return Keyphrase.fromParcel(in);
}
public Keyphrase[] newArray(int size) {
return new Keyphrase[size];
}
};
private static Keyphrase fromParcel(Parcel in) {
int id = in.readInt();
int recognitionModes = in.readInt();
String locale = in.readString();
String text = in.readString();
int[] users = null;
int numUsers = in.readInt();
if (numUsers > 0) {
users = new int[numUsers];
in.readIntArray(users);
}
return new Keyphrase(id, recognitionModes, locale, text, users);
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(id);
dest.writeInt(recognitionModes);
dest.writeString(locale);
dest.writeString(text);
if (users != null) {
dest.writeInt(users.length);
dest.writeIntArray(users);
} else {
dest.writeInt(0);
}
}
@Override
public int describeContents() {
return 0;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((text == null) ? 0 : text.hashCode());
result = prime * result + id;
result = prime * result + ((locale == null) ? 0 : locale.hashCode());
result = prime * result + recognitionModes;
result = prime * result + Arrays.hashCode(users);
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Keyphrase other = (Keyphrase) obj;
if (text == null) {
if (other.text != null)
return false;
} else if (!text.equals(other.text))
return false;
if (id != other.id)
return false;
if (locale == null) {
if (other.locale != null)
return false;
} else if (!locale.equals(other.locale))
return false;
if (recognitionModes != other.recognitionModes)
return false;
if (!Arrays.equals(users, other.users))
return false;
return true;
}
@Override
public String toString() {
return "Keyphrase [id=" + id + ", recognitionModes=" + recognitionModes + ", locale="
+ locale + ", text=" + text + ", users=" + Arrays.toString(users) + "]";
}
}
/*****************************************************************************
@@ -168,7 +325,7 @@ public class SoundTrigger {
* It contains data needed by the hardware to detect a certain number of key phrases
* and the list of corresponding {@link Keyphrase} descriptors.
****************************************************************************/
public static class KeyphraseSoundModel extends SoundModel {
public static class KeyphraseSoundModel extends SoundModel implements Parcelable {
/** Key phrases in this sound model */
public final Keyphrase[] keyphrases; // keyword phrases in model
@@ -176,6 +333,46 @@ public class SoundTrigger {
super(id, TYPE_KEYPHRASE, data);
this.keyphrases = keyphrases;
}
public static final Parcelable.Creator<KeyphraseSoundModel> CREATOR
= new Parcelable.Creator<KeyphraseSoundModel>() {
public KeyphraseSoundModel createFromParcel(Parcel in) {
return KeyphraseSoundModel.fromParcel(in);
}
public KeyphraseSoundModel[] newArray(int size) {
return new KeyphraseSoundModel[size];
}
};
private static KeyphraseSoundModel fromParcel(Parcel in) {
UUID uuid = UUID.fromString(in.readString());
byte[] data = null;
int dataLength = in.readInt();
if (dataLength > 0) {
data = new byte[dataLength];
in.readByteArray(data);
}
Keyphrase[] keyphrases = in.createTypedArray(Keyphrase.CREATOR);
return new KeyphraseSoundModel(uuid, data, keyphrases);
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(uuid.toString());
if (data != null) {
dest.writeInt(data.length);
dest.writeByteArray(data);
} else {
dest.writeInt(0);
}
dest.writeTypedArray(keyphrases, 0);
}
}
/**
@@ -239,7 +436,7 @@ public class SoundTrigger {
* {@link SoundTriggerModule#startRecognition(int, RecognitionConfig)} to configure the
* recognition request.
*/
public static class RecognitionConfig {
public static class RecognitionConfig implements Parcelable {
/** True if the DSP should capture the trigger sound and make it available for further
* capture. */
public final boolean captureRequested;
@@ -256,6 +453,47 @@ public class SoundTrigger {
this.keyphrases = keyphrases;
this.data = data;
}
public static final Parcelable.Creator<RecognitionConfig> CREATOR
= new Parcelable.Creator<RecognitionConfig>() {
public RecognitionConfig createFromParcel(Parcel in) {
return RecognitionConfig.fromParcel(in);
}
public RecognitionConfig[] newArray(int size) {
return new RecognitionConfig[size];
}
};
private static RecognitionConfig fromParcel(Parcel in) {
boolean captureRequested = in.readByte() == 1;
KeyphraseRecognitionExtra[] keyphrases =
in.createTypedArray(KeyphraseRecognitionExtra.CREATOR);
byte[] data = null;
int dataLength = in.readInt();
if (dataLength > 0) {
data = new byte[dataLength];
in.readByteArray(data);
}
return new RecognitionConfig(captureRequested, keyphrases, data);
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeByte((byte) (captureRequested ? 1 : 0));
dest.writeTypedArray(keyphrases, 0);
if (data != null) {
dest.writeInt(data.length);
dest.writeByteArray(data);
} else {
dest.writeInt(0);
}
}
@Override
public int describeContents() {
return 0;
}
}
/**
@@ -266,7 +504,7 @@ public class SoundTrigger {
* should trigger a recognition.
* - The user ID is derived from the system ID {@link android.os.UserHandle#getIdentifier()}.
*/
public static class ConfidenceLevel {
public static class ConfidenceLevel implements Parcelable {
public final int userId;
public final int confidenceLevel;
@@ -274,14 +512,42 @@ public class SoundTrigger {
this.userId = userId;
this.confidenceLevel = confidenceLevel;
}
public static final Parcelable.Creator<ConfidenceLevel> CREATOR
= new Parcelable.Creator<ConfidenceLevel>() {
public ConfidenceLevel createFromParcel(Parcel in) {
return ConfidenceLevel.fromParcel(in);
}
public ConfidenceLevel[] newArray(int size) {
return new ConfidenceLevel[size];
}
};
private static ConfidenceLevel fromParcel(Parcel in) {
int userId = in.readInt();
int confidenceLevel = in.readInt();
return new ConfidenceLevel(userId, confidenceLevel);
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(userId);
dest.writeInt(confidenceLevel);
}
@Override
public int describeContents() {
return 0;
}
}
/**
* Additional data conveyed by a {@link KeyphraseRecognitionEvent}
* for a key phrase detection.
*/
public static class KeyphraseRecognitionExtra {
/** The keyphrse ID */
public static class KeyphraseRecognitionExtra implements Parcelable {
/** The keyphrase ID */
public final int id;
/** Recognition modes matched for this event */
@@ -297,6 +563,36 @@ public class SoundTrigger {
this.recognitionModes = recognitionModes;
this.confidenceLevels = confidenceLevels;
}
public static final Parcelable.Creator<KeyphraseRecognitionExtra> CREATOR
= new Parcelable.Creator<KeyphraseRecognitionExtra>() {
public KeyphraseRecognitionExtra createFromParcel(Parcel in) {
return KeyphraseRecognitionExtra.fromParcel(in);
}
public KeyphraseRecognitionExtra[] newArray(int size) {
return new KeyphraseRecognitionExtra[size];
}
};
private static KeyphraseRecognitionExtra fromParcel(Parcel in) {
int id = in.readInt();
int recognitionModes = in.readInt();
ConfidenceLevel[] confidenceLevels = in.createTypedArray(ConfidenceLevel.CREATOR);
return new KeyphraseRecognitionExtra(id, recognitionModes, confidenceLevels);
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(id);
dest.writeInt(recognitionModes);
dest.writeTypedArray(confidenceLevels, 0);
}
@Override
public int describeContents() {
return 0;
}
}
/**

View File

@@ -17,14 +17,15 @@
package android.service.voice;
import android.content.Intent;
import android.hardware.soundtrigger.Keyphrase;
import android.hardware.soundtrigger.IRecognitionStatusCallback;
import android.hardware.soundtrigger.KeyphraseEnrollmentInfo;
import android.hardware.soundtrigger.KeyphraseMetadata;
import android.hardware.soundtrigger.KeyphraseSoundModel;
import android.hardware.soundtrigger.SoundTrigger;
import android.hardware.soundtrigger.SoundTrigger.ConfidenceLevel;
import android.hardware.soundtrigger.SoundTrigger.Keyphrase;
import android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra;
import android.hardware.soundtrigger.SoundTriggerHelper;
import android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel;
import android.hardware.soundtrigger.SoundTrigger.ModuleProperties;
import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig;
import android.os.RemoteException;
import android.util.Slog;
@@ -68,8 +69,8 @@ public class AlwaysOnHotwordDetector {
/**
* Return codes for {@link #startRecognition(int)}, {@link #stopRecognition()}
*/
public static final int STATUS_ERROR = Integer.MIN_VALUE;
public static final int STATUS_OK = 1;
public static final int STATUS_ERROR = SoundTrigger.STATUS_ERROR;
public static final int STATUS_OK = SoundTrigger.STATUS_OK;
//---- Keyphrase recognition status ----//
/** Indicates that recognition is not available. */
@@ -117,11 +118,10 @@ public class AlwaysOnHotwordDetector {
*/
private final KeyphraseSoundModel mEnrolledSoundModel;
private final KeyphraseEnrollmentInfo mKeyphraseEnrollmentInfo;
private final SoundTriggerHelper mSoundTriggerHelper;
private final SoundTriggerHelper.Listener mListener;
private final int mAvailability;
private final IVoiceInteractionService mVoiceInteractionService;
private final IVoiceInteractionManagerService mModelManagementService;
private final SoundTriggerListener mInternalCallback;
private int mRecognitionState;
@@ -157,15 +157,13 @@ public class AlwaysOnHotwordDetector {
*/
public AlwaysOnHotwordDetector(String text, String locale, Callback callback,
KeyphraseEnrollmentInfo keyphraseEnrollmentInfo,
SoundTriggerHelper soundTriggerHelper,
IVoiceInteractionService voiceInteractionService,
IVoiceInteractionManagerService modelManagementService) {
mText = text;
mLocale = locale;
mKeyphraseEnrollmentInfo = keyphraseEnrollmentInfo;
mKeyphraseMetadata = mKeyphraseEnrollmentInfo.getKeyphraseMetadata(text, locale);
mListener = new SoundTriggerListener(callback);
mSoundTriggerHelper = soundTriggerHelper;
mInternalCallback = new SoundTriggerListener(callback);
mVoiceInteractionService = voiceInteractionService;
mModelManagementService = modelManagementService;
if (mKeyphraseMetadata != null) {
@@ -225,7 +223,7 @@ public class AlwaysOnHotwordDetector {
* @param recognitionFlags The flags to control the recognition properties.
* The allowed flags are {@link #RECOGNITION_FLAG_NONE} and
* {@link #RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO}.
* @return One of {@link #STATUS_ERROR} or {@link #STATUS_OK}.
* @return {@link #STATUS_OK} if the call succeeds, an error code otherwise.
* @throws UnsupportedOperationException if the recognition isn't supported.
* Callers should check the availability by calling {@link #getAvailability()}
* before calling this method to avoid this exception.
@@ -245,22 +243,25 @@ public class AlwaysOnHotwordDetector {
mKeyphraseMetadata.recognitionModeFlags, new ConfidenceLevel[0]);
boolean captureTriggerAudio =
(recognitionFlags & RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO) != 0;
int code = mSoundTriggerHelper.startRecognition(mKeyphraseMetadata.id,
mEnrolledSoundModel.convertToSoundTriggerKeyphraseSoundModel(), mListener,
new RecognitionConfig(
captureTriggerAudio, recognitionExtra,null /* additional data */));
if (code != SoundTriggerHelper.STATUS_OK) {
Slog.w(TAG, "startRecognition() failed with error code " + code);
return STATUS_ERROR;
} else {
return STATUS_OK;
int code = STATUS_ERROR;
try {
code = mModelManagementService.startRecognition(mVoiceInteractionService,
mKeyphraseMetadata.id, mEnrolledSoundModel, mInternalCallback,
new RecognitionConfig(
captureTriggerAudio, recognitionExtra, null /* additional data */));
} catch (RemoteException e) {
Slog.w(TAG, "RemoteException in startRecognition!");
}
if (code != STATUS_OK) {
Slog.w(TAG, "startRecognition() failed with error code " + code);
}
return code;
}
/**
* Stops recognition for the associated keyphrase.
*
* @return One of {@link #STATUS_ERROR} or {@link #STATUS_OK}.
* @return {@link #STATUS_OK} if the call succeeds, an error code otherwise.
* @throws UnsupportedOperationException if the recognition isn't supported.
* Callers should check the availability by calling {@link #getAvailability()}
* before calling this method to avoid this exception.
@@ -272,14 +273,18 @@ public class AlwaysOnHotwordDetector {
}
mRecognitionState &= ~RECOGNITION_STATUS_NOT_REQUESTED;
int code = mSoundTriggerHelper.stopRecognition(mKeyphraseMetadata.id, mListener);
if (code != SoundTriggerHelper.STATUS_OK) {
Slog.w(TAG, "stopRecognition() failed with error code " + code);
return STATUS_ERROR;
} else {
return STATUS_OK;
int code = STATUS_ERROR;
try {
code = mModelManagementService.stopRecognition(
mVoiceInteractionService, mKeyphraseMetadata.id, mInternalCallback);
} catch (RemoteException e) {
Slog.w(TAG, "RemoteException in stopRecognition!");
}
if (code != STATUS_OK) {
Slog.w(TAG, "stopRecognition() failed with error code " + code);
}
return code;
}
/**
@@ -309,8 +314,15 @@ public class AlwaysOnHotwordDetector {
}
private int internalGetAvailability() {
ModuleProperties dspModuleProperties = null;
try {
dspModuleProperties =
mModelManagementService.getDspModuleProperties(mVoiceInteractionService);
} catch (RemoteException e) {
Slog.w(TAG, "RemoteException in getDspProperties!");
}
// No DSP available
if (mSoundTriggerHelper.dspInfo == null) {
if (dspModuleProperties == null) {
mRecognitionState = RECOGNITION_STATUS_NOT_AVAILABLE;
return KEYPHRASE_HARDWARE_UNAVAILABLE;
}
@@ -359,7 +371,7 @@ public class AlwaysOnHotwordDetector {
}
/** @hide */
static final class SoundTriggerListener implements SoundTriggerHelper.Listener {
static final class SoundTriggerListener extends IRecognitionStatusCallback.Stub {
private final Callback mCallback;
public SoundTriggerListener(Callback callback) {
@@ -367,20 +379,21 @@ public class AlwaysOnHotwordDetector {
}
@Override
public void onKeyphraseSpoken(byte[] data) {
public void onDetected(byte[] data) {
Slog.i(TAG, "onKeyphraseSpoken");
mCallback.onDetected(data);
}
@Override
public void onListeningStateChanged(int state) {
Slog.i(TAG, "onListeningStateChanged: state=" + state);
// TODO: Set/unset the RECOGNITION_STATUS_ACTIVE flag here.
if (state == SoundTriggerHelper.STATE_STARTED) {
mCallback.onDetectionStarted();
} else if (state == SoundTriggerHelper.STATE_STOPPED) {
mCallback.onDetectionStopped();
}
public void onDetectionStarted() {
// TODO: Set the RECOGNITION_STATUS_ACTIVE flag here.
mCallback.onDetectionStarted();
}
@Override
public void onDetectionStopped() {
// TODO: Unset the RECOGNITION_STATUS_ACTIVE flag here.
mCallback.onDetectionStopped();
}
}
}

View File

@@ -22,7 +22,6 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.hardware.soundtrigger.KeyphraseEnrollmentInfo;
import android.hardware.soundtrigger.SoundTriggerHelper;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -78,7 +77,6 @@ public class VoiceInteractionService extends Service {
IVoiceInteractionManagerService mSystemService;
private KeyphraseEnrollmentInfo mKeyphraseEnrollmentInfo;
private SoundTriggerHelper mSoundTriggerHelper;
static final int MSG_READY = 1;
@@ -150,7 +148,6 @@ public class VoiceInteractionService extends Service {
mSystemService = IVoiceInteractionManagerService.Stub.asInterface(
ServiceManager.getService(Context.VOICE_INTERACTION_MANAGER_SERVICE));
mKeyphraseEnrollmentInfo = new KeyphraseEnrollmentInfo(getPackageManager());
mSoundTriggerHelper = new SoundTriggerHelper();
}
/**
@@ -168,7 +165,7 @@ public class VoiceInteractionService extends Service {
// TODO: Cache instances and return the same one instead of creating a new interactor
// for the same keyphrase/locale combination.
return new AlwaysOnHotwordDetector(keyphrase, locale, callback,
mKeyphraseEnrollmentInfo, mSoundTriggerHelper, mInterface, mSystemService);
mKeyphraseEnrollmentInfo, mInterface, mSystemService);
}
/**

View File

@@ -20,7 +20,8 @@ import android.content.Intent;
import android.os.Bundle;
import com.android.internal.app.IVoiceInteractor;
import android.hardware.soundtrigger.KeyphraseSoundModel;
import android.hardware.soundtrigger.IRecognitionStatusCallback;
import android.hardware.soundtrigger.SoundTrigger;
import android.service.voice.IVoiceInteractionService;
import android.service.voice.IVoiceInteractionSession;
@@ -37,9 +38,26 @@ interface IVoiceInteractionManagerService {
*
* @param service The current voice interaction service.
*/
List<KeyphraseSoundModel> listRegisteredKeyphraseSoundModels(in IVoiceInteractionService service);
List<SoundTrigger.KeyphraseSoundModel> listRegisteredKeyphraseSoundModels(
in IVoiceInteractionService service);
/**
* Updates the given keyphrase sound model. Adds the model if it doesn't exist currently.
*/
int updateKeyphraseSoundModel(in KeyphraseSoundModel model);
int updateKeyphraseSoundModel(in SoundTrigger.KeyphraseSoundModel model);
/**
* Gets the properties of the DSP hardware on this device, null if not present.
*/
SoundTrigger.ModuleProperties getDspModuleProperties(in IVoiceInteractionService service);
/**
* Starts a recognition for the given keyphrase.
*/
int startRecognition(in IVoiceInteractionService service, int keyphraseId,
in SoundTrigger.KeyphraseSoundModel soundModel, in IRecognitionStatusCallback callback,
in SoundTrigger.RecognitionConfig recognitionConfig);
/**
* Stops a recognition for the given keyphrase.
*/
int stopRecognition(in IVoiceInteractionService service, int keyphraseId,
in IRecognitionStatusCallback callback);
}

View File

@@ -22,8 +22,9 @@ import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.hardware.soundtrigger.SoundTrigger;
import android.hardware.soundtrigger.Keyphrase;
import android.hardware.soundtrigger.KeyphraseSoundModel;
import android.hardware.soundtrigger.SoundTrigger.Keyphrase;
import android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel;
import android.text.TextUtils;
import android.util.Slog;
import java.util.ArrayList;
@@ -39,7 +40,7 @@ public class DatabaseHelper extends SQLiteOpenHelper {
static final String TAG = "SoundModelDBHelper";
private static final String NAME = "sound_model.db";
private static final int VERSION = 1;
private static final int VERSION = 2;
public static interface KeyphraseContract {
public static final String TABLE = "keyphrase";
@@ -63,7 +64,7 @@ public class DatabaseHelper extends SQLiteOpenHelper {
+ KeyphraseContract.TABLE + "("
+ KeyphraseContract.KEY_ID + " INTEGER PRIMARY KEY,"
+ KeyphraseContract.KEY_RECOGNITION_MODES + " INTEGER,"
+ KeyphraseContract.KEY_USERS + " INTEGER,"
+ KeyphraseContract.KEY_USERS + " TEXT,"
+ KeyphraseContract.KEY_SOUND_MODEL_ID + " TEXT,"
+ KeyphraseContract.KEY_LOCALE + " TEXT,"
+ KeyphraseContract.KEY_HINT_TEXT + " TEXT" + ")";
@@ -119,10 +120,11 @@ public class DatabaseHelper extends SQLiteOpenHelper {
private boolean addOrUpdateKeyphrase(SQLiteDatabase db, UUID modelId, Keyphrase keyphrase) {
ContentValues values = new ContentValues();
values.put(KeyphraseContract.KEY_ID, keyphrase.id);
values.put(KeyphraseContract.KEY_RECOGNITION_MODES, keyphrase.recognitionModeFlags);
values.put(KeyphraseContract.KEY_RECOGNITION_MODES, keyphrase.recognitionModes);
values.put(KeyphraseContract.KEY_SOUND_MODEL_ID, modelId.toString());
values.put(KeyphraseContract.KEY_HINT_TEXT, keyphrase.hintText);
values.put(KeyphraseContract.KEY_HINT_TEXT, keyphrase.text);
values.put(KeyphraseContract.KEY_LOCALE, keyphrase.locale);
values.put(KeyphraseContract.KEY_USERS, getCommaSeparatedString(keyphrase.users));
if (db.insertWithOnConflict(
KeyphraseContract.TABLE, null, values, SQLiteDatabase.CONFLICT_REPLACE) != -1) {
return true;
@@ -193,11 +195,12 @@ public class DatabaseHelper extends SQLiteOpenHelper {
do {
int id = c.getInt(c.getColumnIndex(KeyphraseContract.KEY_ID));
int modes = c.getInt(c.getColumnIndex(KeyphraseContract.KEY_RECOGNITION_MODES));
int[] users = {c.getInt(c.getColumnIndex(KeyphraseContract.KEY_USERS))};
int[] users = getArrayForCommaSeparatedString(
c.getString(c.getColumnIndex(KeyphraseContract.KEY_USERS)));
String locale = c.getString(c.getColumnIndex(KeyphraseContract.KEY_LOCALE));
String hintText = c.getString(c.getColumnIndex(KeyphraseContract.KEY_HINT_TEXT));
keyphrases.add(new Keyphrase(id, hintText, locale, modes, users));
keyphrases.add(new Keyphrase(id, modes, locale, hintText, users));
} while (c.moveToNext());
}
Keyphrase[] keyphraseArr = new Keyphrase[keyphrases.size()];
@@ -205,4 +208,29 @@ public class DatabaseHelper extends SQLiteOpenHelper {
c.close();
return keyphraseArr;
}
private String getCommaSeparatedString(int[] users) {
if (users == null || users.length == 0) {
return "";
}
String csv = "";
for (int user : users) {
csv += String.valueOf(user);
csv += ",";
}
return csv.substring(0, csv.length() - 1);
}
private int[] getArrayForCommaSeparatedString(String text) {
if (TextUtils.isEmpty(text)) {
return null;
}
String[] usersStr = text.split(",");
int[] users = new int[usersStr.length];
for (int i = 0; i < usersStr.length; i++) {
users[i] = Integer.valueOf(usersStr[i]);
}
return users;
}
}

View File

@@ -14,11 +14,17 @@
* limitations under the License.
*/
package android.hardware.soundtrigger;
package com.android.server.voiceinteraction;
import android.hardware.soundtrigger.IRecognitionStatusCallback;
import android.hardware.soundtrigger.SoundTrigger;
import android.hardware.soundtrigger.SoundTrigger.Keyphrase;
import android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel;
import android.hardware.soundtrigger.SoundTrigger.ModuleProperties;
import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig;
import android.hardware.soundtrigger.SoundTrigger.RecognitionEvent;
import android.hardware.soundtrigger.SoundTriggerModule;
import android.os.RemoteException;
import android.util.Slog;
import android.util.SparseArray;
@@ -36,72 +42,45 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
static final int TEMP_KEYPHRASE_ID = 1;
/**
* Return codes for {@link #startRecognition(Keyphrase)}, {@link #stopRecognition(Keyphrase)}
* Note: Keep in sync with AlwaysOnKeyphraseInteractor.java
* Return codes for {@link #startRecognition(int, KeyphraseSoundModel,
* IRecognitionStatusCallback, RecognitionConfig)},
* {@link #stopRecognition(int, IRecognitionStatusCallback)}
*/
public static final int STATUS_ERROR = Integer.MIN_VALUE;
public static final int STATUS_OK = 1;
/**
* States for {@link Listener#onListeningStateChanged(int, int)}.
*/
public static final int STATE_STOPPED = 0;
public static final int STATE_STARTED = 1;
public static final int STATUS_ERROR = SoundTrigger.STATUS_ERROR;
public static final int STATUS_OK = SoundTrigger.STATUS_OK;
private static final int INVALID_SOUND_MODEL_HANDLE = -1;
/** The {@link DspInfo} for the system, or null if none exists. */
public final DspInfo dspInfo;
final ModuleProperties moduleProperties;
/** The properties for the DSP module */
private final ModuleProperties mModuleProperties;
private final SoundTriggerModule mModule;
private final SparseArray<Listener> mActiveListeners;
// Use a RemoteCallbackList here?
private final SparseArray<IRecognitionStatusCallback> mActiveListeners;
private int mCurrentSoundModelHandle = INVALID_SOUND_MODEL_HANDLE;
/**
* The callback for sound trigger events.
*/
public interface Listener {
/**
* Called when the given keyphrase is spoken.
*
* @param data The captured audio, may be null.
*/
void onKeyphraseSpoken(byte[] data);
/**
* Called when the listening state for the given keyphrase changes.
* @param state Indicates the current state.
*/
void onListeningStateChanged(int state);
}
public SoundTriggerHelper() {
SoundTriggerHelper() {
ArrayList <ModuleProperties> modules = new ArrayList<>();
int status = SoundTrigger.listModules(modules);
mActiveListeners = new SparseArray<>(1);
if (status != SoundTrigger.STATUS_OK || modules.size() == 0) {
Slog.w(TAG, "listModules status=" + status + ", # of modules=" + modules.size());
dspInfo = null;
mModuleProperties = null;
moduleProperties = null;
mModule = null;
} else {
// TODO: Figure out how to determine which module corresponds to the DSP hardware.
mModuleProperties = modules.get(0);
dspInfo = new DspInfo(mModuleProperties.uuid, mModuleProperties.implementor,
mModuleProperties.description, mModuleProperties.version,
mModuleProperties.powerConsumptionMw);
mModule = SoundTrigger.attachModule(mModuleProperties.id, this, null);
moduleProperties = modules.get(0);
mModule = SoundTrigger.attachModule(moduleProperties.id, this, null);
}
}
/**
* @return True, if a recognition for the given {@link Keyphrase} is active.
*/
public synchronized boolean isKeyphraseActive(Keyphrase keyphrase) {
synchronized boolean isKeyphraseActive(Keyphrase keyphrase) {
if (keyphrase == null) {
Slog.w(TAG, "isKeyphraseActive requires a non-null keyphrase");
return false;
@@ -110,7 +89,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
}
/**
* Starts recognition for the given {@link Keyphrase}.
* Starts recognition for the given keyphraseId.
*
* @param keyphraseId The identifier of the keyphrase for which
* the recognition is to be started.
@@ -118,22 +97,27 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
* @param listener The listener for the recognition events related to the given keyphrase.
* @return One of {@link #STATUS_ERROR} or {@link #STATUS_OK}.
*/
public synchronized int startRecognition(int keyphraseId,
SoundTrigger.KeyphraseSoundModel soundModel,
Listener listener, RecognitionConfig recognitionConfig) {
if (dspInfo == null || mModule == null) {
synchronized int startRecognition(int keyphraseId,
KeyphraseSoundModel soundModel,
IRecognitionStatusCallback listener,
RecognitionConfig recognitionConfig) {
if (moduleProperties == null || mModule == null) {
Slog.w(TAG, "Attempting startRecognition without the capability");
return STATUS_ERROR;
}
Listener oldListener = mActiveListeners.get(keyphraseId);
IRecognitionStatusCallback oldListener = mActiveListeners.get(keyphraseId);
if (oldListener != null && oldListener != listener) {
if (mCurrentSoundModelHandle != INVALID_SOUND_MODEL_HANDLE) {
Slog.w(TAG, "Canceling previous recognition");
// TODO: Inspect the return codes here.
mModule.unloadSoundModel(mCurrentSoundModelHandle);
}
mActiveListeners.get(keyphraseId).onListeningStateChanged(STATE_STOPPED);
try {
mActiveListeners.get(keyphraseId).onDetectionStopped();
} catch (RemoteException e) {
Slog.w(TAG, "RemoteException in onDetectionStopped");
}
mActiveListeners.remove(keyphraseId);
}
@@ -165,7 +149,8 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
}
/**
* Stops recognition for the given {@link Keyphrase} if a recognition is currently active.
* Stops recognition for the given {@link Keyphrase} if a recognition is
* currently active.
*
* @param keyphraseId The identifier of the keyphrase for which
* the recognition is to be stopped.
@@ -173,13 +158,13 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
*
* @return One of {@link #STATUS_ERROR} or {@link #STATUS_OK}.
*/
public synchronized int stopRecognition(int keyphraseId, Listener listener) {
if (dspInfo == null || mModule == null) {
synchronized int stopRecognition(int keyphraseId, IRecognitionStatusCallback listener) {
if (moduleProperties == null || mModule == null) {
Slog.w(TAG, "Attempting stopRecognition without the capability");
return STATUS_ERROR;
}
Listener currentListener = mActiveListeners.get(keyphraseId);
IRecognitionStatusCallback currentListener = mActiveListeners.get(keyphraseId);
if (currentListener == null) {
// startRecognition hasn't been called or it failed.
Slog.w(TAG, "Attempting stopRecognition without a successful startRecognition");
@@ -217,7 +202,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
// TODO: Get the keyphrase out of the event and fire events on it.
// For now, as a nasty workaround, we fire all events to the listener for
// keyphrase with TEMP_KEYPHRASE_ID.
Listener listener = null;
IRecognitionStatusCallback listener = null;
synchronized(this) {
// TODO: The keyphrase should come from the recognition event
// as it may be for a different keyphrase than the current one.
@@ -231,13 +216,25 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
switch (event.status) {
case SoundTrigger.RECOGNITION_STATUS_SUCCESS:
// TODO: Pass the captured audio back.
listener.onKeyphraseSpoken(null);
try {
listener.onDetected(null);
} catch (RemoteException e) {
Slog.w(TAG, "RemoteException in onDetected");
}
break;
case SoundTrigger.RECOGNITION_STATUS_ABORT:
listener.onListeningStateChanged(STATE_STOPPED);
try {
listener.onDetectionStopped();
} catch (RemoteException e) {
Slog.w(TAG, "RemoteException in onDetectionStopped");
}
break;
case SoundTrigger.RECOGNITION_STATUS_FAILURE:
listener.onListeningStateChanged(STATE_STOPPED);
try {
listener.onDetectionStopped();
} catch (RemoteException e) {
Slog.w(TAG, "RemoteException in onDetectionStopped");
}
break;
}
}

View File

@@ -24,7 +24,10 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.ContentObserver;
import android.hardware.soundtrigger.KeyphraseSoundModel;
import android.hardware.soundtrigger.IRecognitionStatusCallback;
import android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel;
import android.hardware.soundtrigger.SoundTrigger.ModuleProperties;
import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
@@ -56,19 +59,17 @@ public class VoiceInteractionManagerService extends SystemService {
static final String TAG = "VoiceInteractionManagerService";
// TODO: Add descriptive error codes.
public static final int STATUS_ERROR = -1;
public static final int STATUS_OK = 1;
final Context mContext;
final ContentResolver mResolver;
final DatabaseHelper mDbHelper;
final SoundTriggerHelper mSoundTriggerHelper;
public VoiceInteractionManagerService(Context context) {
super(context);
mContext = context;
mResolver = context.getContentResolver();
mDbHelper = new DatabaseHelper(context);
mSoundTriggerHelper = new SoundTriggerHelper();
}
@Override
@@ -239,6 +240,8 @@ public class VoiceInteractionManagerService extends SystemService {
}
}
//----------------- Model management APIs --------------------------------//
@Override
public List<KeyphraseSoundModel> listRegisteredKeyphraseSoundModels(
IVoiceInteractionService service) {
@@ -290,15 +293,15 @@ public class VoiceInteractionManagerService extends SystemService {
// If the keyphrases are not present in the model, delete the model.
if (model.keyphrases == null) {
if (mDbHelper.deleteKeyphraseSoundModel(model.uuid)) {
return STATUS_OK;
return SoundTriggerHelper.STATUS_OK;
} else {
return STATUS_ERROR;
return SoundTriggerHelper.STATUS_ERROR;
}
} else {
if (mDbHelper.addOrUpdateKeyphraseSoundModel(model)) {
return STATUS_OK;
return SoundTriggerHelper.STATUS_OK;
} else {
return STATUS_ERROR;
return SoundTriggerHelper.STATUS_ERROR;
}
}
} finally {
@@ -307,6 +310,68 @@ public class VoiceInteractionManagerService extends SystemService {
}
}
//----------------- SoundTrigger APIs --------------------------------//
@Override
public ModuleProperties getDspModuleProperties(IVoiceInteractionService service) {
// Allow the call if this is the current voice interaction service.
synchronized (this) {
if (mImpl == null || mImpl.mService == null
|| service == null || service.asBinder() != mImpl.mService.asBinder()) {
throw new SecurityException(
"Caller is not the current voice interaction service");
}
final long caller = Binder.clearCallingIdentity();
try {
return mSoundTriggerHelper.moduleProperties;
} finally {
Binder.restoreCallingIdentity(caller);
}
}
}
@Override
public int startRecognition(IVoiceInteractionService service, int keyphraseId,
KeyphraseSoundModel soundModel, IRecognitionStatusCallback callback,
RecognitionConfig recognitionConfig) {
// Allow the call if this is the current voice interaction service.
synchronized (this) {
if (mImpl == null || mImpl.mService == null
|| service == null || service.asBinder() != mImpl.mService.asBinder()) {
throw new SecurityException(
"Caller is not the current voice interaction service");
}
final long caller = Binder.clearCallingIdentity();
try {
return mSoundTriggerHelper.startRecognition(
keyphraseId, soundModel, callback, recognitionConfig);
} finally {
Binder.restoreCallingIdentity(caller);
}
}
}
@Override
public int stopRecognition(IVoiceInteractionService service, int keyphraseId,
IRecognitionStatusCallback callback) {
// Allow the call if this is the current voice interaction service.
synchronized (this) {
if (mImpl == null || mImpl.mService == null
|| service == null || service.asBinder() != mImpl.mService.asBinder()) {
throw new SecurityException(
"Caller is not the current voice interaction service");
}
final long caller = Binder.clearCallingIdentity();
try {
return mSoundTriggerHelper.stopRecognition(keyphraseId, callback);
} finally {
Binder.restoreCallingIdentity(caller);
}
}
}
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (mContext.checkCallingOrSelfPermission(Manifest.permission.DUMP)