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:
@@ -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 \
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
package android.hardware.soundtrigger;
|
||||
|
||||
// @hide
|
||||
parcelable Keyphrase;
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
package android.hardware.soundtrigger;
|
||||
|
||||
// @hide
|
||||
parcelable KeyphraseSoundModel;
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
24
core/java/android/hardware/soundtrigger/SoundTrigger.aidl
Normal file
24
core/java/android/hardware/soundtrigger/SoundTrigger.aidl
Normal 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;
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user