Migrate SoundTrigger implementation to new service
The API offered by SoundTrigger.java is now implemented on top of the new soundtrigger_middleware service. There is no longer any need for JNI - the API now talks directly with the AIDL interface of the new service. In the process, some annotations and input validation have been added to improve the overall quality of this API. Change-Id: I731ffd5a275b88f38d84dd3daa022a13f97a5ee1 Bug: 142070343
This commit is contained in:
329
core/java/android/hardware/soundtrigger/ConversionUtil.java
Normal file
329
core/java/android/hardware/soundtrigger/ConversionUtil.java
Normal file
@@ -0,0 +1,329 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.hardware.soundtrigger.ModelParams;
|
||||
import android.media.AudioFormat;
|
||||
import android.media.audio.common.AudioConfig;
|
||||
import android.media.soundtrigger_middleware.ConfidenceLevel;
|
||||
import android.media.soundtrigger_middleware.ModelParameterRange;
|
||||
import android.media.soundtrigger_middleware.Phrase;
|
||||
import android.media.soundtrigger_middleware.PhraseRecognitionEvent;
|
||||
import android.media.soundtrigger_middleware.PhraseRecognitionExtra;
|
||||
import android.media.soundtrigger_middleware.PhraseSoundModel;
|
||||
import android.media.soundtrigger_middleware.RecognitionConfig;
|
||||
import android.media.soundtrigger_middleware.RecognitionEvent;
|
||||
import android.media.soundtrigger_middleware.RecognitionMode;
|
||||
import android.media.soundtrigger_middleware.SoundModel;
|
||||
import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
|
||||
import android.media.soundtrigger_middleware.SoundTriggerModuleProperties;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.UUID;
|
||||
|
||||
/** @hide */
|
||||
class ConversionUtil {
|
||||
public static SoundTrigger.ModuleProperties aidl2apiModuleDescriptor(
|
||||
SoundTriggerModuleDescriptor aidlDesc) {
|
||||
SoundTriggerModuleProperties properties = aidlDesc.properties;
|
||||
return new SoundTrigger.ModuleProperties(
|
||||
aidlDesc.handle,
|
||||
properties.implementor,
|
||||
properties.description,
|
||||
properties.uuid,
|
||||
properties.version,
|
||||
properties.maxSoundModels,
|
||||
properties.maxKeyPhrases,
|
||||
properties.maxUsers,
|
||||
aidl2apiRecognitionModes(properties.recognitionModes),
|
||||
properties.captureTransition,
|
||||
properties.maxBufferMs,
|
||||
properties.concurrentCapture,
|
||||
properties.powerConsumptionMw,
|
||||
properties.triggerInEvent
|
||||
);
|
||||
}
|
||||
|
||||
public static int aidl2apiRecognitionModes(int aidlModes) {
|
||||
int result = 0;
|
||||
if ((aidlModes & RecognitionMode.VOICE_TRIGGER) != 0) {
|
||||
result |= SoundTrigger.RECOGNITION_MODE_VOICE_TRIGGER;
|
||||
}
|
||||
if ((aidlModes & RecognitionMode.USER_IDENTIFICATION) != 0) {
|
||||
result |= SoundTrigger.RECOGNITION_MODE_USER_IDENTIFICATION;
|
||||
}
|
||||
if ((aidlModes & RecognitionMode.USER_AUTHENTICATION) != 0) {
|
||||
result |= SoundTrigger.RECOGNITION_MODE_USER_AUTHENTICATION;
|
||||
}
|
||||
if ((aidlModes & RecognitionMode.GENERIC_TRIGGER) != 0) {
|
||||
result |= SoundTrigger.RECOGNITION_MODE_GENERIC;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static int api2aidlRecognitionModes(int apiModes) {
|
||||
int result = 0;
|
||||
if ((apiModes & SoundTrigger.RECOGNITION_MODE_VOICE_TRIGGER) != 0) {
|
||||
result |= RecognitionMode.VOICE_TRIGGER;
|
||||
}
|
||||
if ((apiModes & SoundTrigger.RECOGNITION_MODE_USER_IDENTIFICATION) != 0) {
|
||||
result |= RecognitionMode.USER_IDENTIFICATION;
|
||||
}
|
||||
if ((apiModes & SoundTrigger.RECOGNITION_MODE_USER_AUTHENTICATION) != 0) {
|
||||
result |= RecognitionMode.USER_AUTHENTICATION;
|
||||
}
|
||||
if ((apiModes & SoundTrigger.RECOGNITION_MODE_GENERIC) != 0) {
|
||||
result |= RecognitionMode.GENERIC_TRIGGER;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
public static SoundModel api2aidlGenericSoundModel(SoundTrigger.GenericSoundModel apiModel) {
|
||||
return api2aidlSoundModel(apiModel);
|
||||
}
|
||||
|
||||
public static SoundModel api2aidlSoundModel(SoundTrigger.SoundModel apiModel) {
|
||||
SoundModel aidlModel = new SoundModel();
|
||||
aidlModel.type = apiModel.type;
|
||||
aidlModel.uuid = api2aidlUuid(apiModel.uuid);
|
||||
aidlModel.vendorUuid = api2aidlUuid(apiModel.vendorUuid);
|
||||
aidlModel.data = Arrays.copyOf(apiModel.data, apiModel.data.length);
|
||||
return aidlModel;
|
||||
}
|
||||
|
||||
public static String api2aidlUuid(UUID apiUuid) {
|
||||
return apiUuid.toString();
|
||||
}
|
||||
|
||||
public static PhraseSoundModel api2aidlPhraseSoundModel(
|
||||
SoundTrigger.KeyphraseSoundModel apiModel) {
|
||||
PhraseSoundModel aidlModel = new PhraseSoundModel();
|
||||
aidlModel.common = api2aidlSoundModel(apiModel);
|
||||
aidlModel.phrases = new Phrase[apiModel.keyphrases.length];
|
||||
for (int i = 0; i < apiModel.keyphrases.length; ++i) {
|
||||
aidlModel.phrases[i] = api2aidlPhrase(apiModel.keyphrases[i]);
|
||||
}
|
||||
return aidlModel;
|
||||
}
|
||||
|
||||
public static Phrase api2aidlPhrase(SoundTrigger.Keyphrase apiPhrase) {
|
||||
Phrase aidlPhrase = new Phrase();
|
||||
aidlPhrase.id = apiPhrase.id;
|
||||
aidlPhrase.recognitionModes = api2aidlRecognitionModes(apiPhrase.recognitionModes);
|
||||
aidlPhrase.users = Arrays.copyOf(apiPhrase.users, apiPhrase.users.length);
|
||||
aidlPhrase.locale = apiPhrase.locale;
|
||||
aidlPhrase.text = apiPhrase.text;
|
||||
return aidlPhrase;
|
||||
}
|
||||
|
||||
public static RecognitionConfig api2aidlRecognitionConfig(
|
||||
SoundTrigger.RecognitionConfig apiConfig) {
|
||||
RecognitionConfig aidlConfig = new RecognitionConfig();
|
||||
aidlConfig.captureRequested = apiConfig.captureRequested;
|
||||
// apiConfig.allowMultipleTriggers is ignored by the lower layers.
|
||||
aidlConfig.phraseRecognitionExtras =
|
||||
new PhraseRecognitionExtra[apiConfig.keyphrases.length];
|
||||
for (int i = 0; i < apiConfig.keyphrases.length; ++i) {
|
||||
aidlConfig.phraseRecognitionExtras[i] = api2aidlPhraseRecognitionExtra(
|
||||
apiConfig.keyphrases[i]);
|
||||
}
|
||||
aidlConfig.data = Arrays.copyOf(apiConfig.data, apiConfig.data.length);
|
||||
return aidlConfig;
|
||||
}
|
||||
|
||||
public static PhraseRecognitionExtra api2aidlPhraseRecognitionExtra(
|
||||
SoundTrigger.KeyphraseRecognitionExtra apiExtra) {
|
||||
PhraseRecognitionExtra aidlExtra = new PhraseRecognitionExtra();
|
||||
aidlExtra.id = apiExtra.id;
|
||||
aidlExtra.recognitionModes = api2aidlRecognitionModes(apiExtra.recognitionModes);
|
||||
aidlExtra.confidenceLevel = apiExtra.coarseConfidenceLevel;
|
||||
aidlExtra.levels = new ConfidenceLevel[apiExtra.confidenceLevels.length];
|
||||
for (int i = 0; i < apiExtra.confidenceLevels.length; ++i) {
|
||||
aidlExtra.levels[i] = api2aidlConfidenceLevel(apiExtra.confidenceLevels[i]);
|
||||
}
|
||||
return aidlExtra;
|
||||
}
|
||||
|
||||
public static SoundTrigger.KeyphraseRecognitionExtra aidl2apiPhraseRecognitionExtra(
|
||||
PhraseRecognitionExtra aidlExtra) {
|
||||
SoundTrigger.ConfidenceLevel[] apiLevels =
|
||||
new SoundTrigger.ConfidenceLevel[aidlExtra.levels.length];
|
||||
for (int i = 0; i < aidlExtra.levels.length; ++i) {
|
||||
apiLevels[i] = aidl2apiConfidenceLevel(aidlExtra.levels[i]);
|
||||
}
|
||||
return new SoundTrigger.KeyphraseRecognitionExtra(aidlExtra.id,
|
||||
aidl2apiRecognitionModes(aidlExtra.recognitionModes),
|
||||
aidlExtra.confidenceLevel, apiLevels);
|
||||
}
|
||||
|
||||
public static ConfidenceLevel api2aidlConfidenceLevel(
|
||||
SoundTrigger.ConfidenceLevel apiLevel) {
|
||||
ConfidenceLevel aidlLevel = new ConfidenceLevel();
|
||||
aidlLevel.levelPercent = apiLevel.confidenceLevel;
|
||||
aidlLevel.userId = apiLevel.userId;
|
||||
return aidlLevel;
|
||||
}
|
||||
|
||||
public static SoundTrigger.ConfidenceLevel aidl2apiConfidenceLevel(
|
||||
ConfidenceLevel apiLevel) {
|
||||
return new SoundTrigger.ConfidenceLevel(apiLevel.userId, apiLevel.levelPercent);
|
||||
}
|
||||
|
||||
public static SoundTrigger.RecognitionEvent aidl2apiRecognitionEvent(
|
||||
int modelHandle, RecognitionEvent aidlEvent) {
|
||||
return new SoundTrigger.GenericRecognitionEvent(
|
||||
aidlEvent.status,
|
||||
modelHandle, aidlEvent.captureAvailable, aidlEvent.captureSession,
|
||||
aidlEvent.captureDelayMs, aidlEvent.capturePreambleMs, aidlEvent.triggerInData,
|
||||
aidl2apiAudioFormat(aidlEvent.audioConfig), aidlEvent.data);
|
||||
}
|
||||
|
||||
public static SoundTrigger.RecognitionEvent aidl2apiPhraseRecognitionEvent(
|
||||
int modelHandle,
|
||||
PhraseRecognitionEvent aidlEvent) {
|
||||
SoundTrigger.KeyphraseRecognitionExtra[] apiExtras =
|
||||
new SoundTrigger.KeyphraseRecognitionExtra[aidlEvent.phraseExtras.length];
|
||||
for (int i = 0; i < aidlEvent.phraseExtras.length; ++i) {
|
||||
apiExtras[i] = aidl2apiPhraseRecognitionExtra(aidlEvent.phraseExtras[i]);
|
||||
}
|
||||
return new SoundTrigger.KeyphraseRecognitionEvent(aidlEvent.common.status, modelHandle,
|
||||
aidlEvent.common.captureAvailable,
|
||||
aidlEvent.common.captureSession, aidlEvent.common.captureDelayMs,
|
||||
aidlEvent.common.capturePreambleMs, aidlEvent.common.triggerInData,
|
||||
aidl2apiAudioFormat(aidlEvent.common.audioConfig), aidlEvent.common.data,
|
||||
apiExtras);
|
||||
}
|
||||
|
||||
public static AudioFormat aidl2apiAudioFormat(AudioConfig audioConfig) {
|
||||
AudioFormat.Builder apiBuilder = new AudioFormat.Builder();
|
||||
apiBuilder.setSampleRate(audioConfig.sampleRateHz);
|
||||
apiBuilder.setChannelMask(aidl2apiChannelInMask(audioConfig.channelMask));
|
||||
apiBuilder.setEncoding(aidl2apiEncoding(audioConfig.format));
|
||||
return apiBuilder.build();
|
||||
}
|
||||
|
||||
public static int aidl2apiEncoding(int aidlFormat) {
|
||||
switch (aidlFormat) {
|
||||
case android.media.audio.common.AudioFormat.PCM
|
||||
| android.media.audio.common.AudioFormat.PCM_SUB_16_BIT:
|
||||
return AudioFormat.ENCODING_PCM_16BIT;
|
||||
|
||||
case android.media.audio.common.AudioFormat.PCM
|
||||
| android.media.audio.common.AudioFormat.PCM_SUB_8_BIT:
|
||||
return AudioFormat.ENCODING_PCM_8BIT;
|
||||
|
||||
case android.media.audio.common.AudioFormat.PCM
|
||||
| android.media.audio.common.AudioFormat.PCM_SUB_FLOAT:
|
||||
case android.media.audio.common.AudioFormat.PCM
|
||||
| android.media.audio.common.AudioFormat.PCM_SUB_8_24_BIT:
|
||||
case android.media.audio.common.AudioFormat.PCM
|
||||
| android.media.audio.common.AudioFormat.PCM_SUB_24_BIT_PACKED:
|
||||
case android.media.audio.common.AudioFormat.PCM
|
||||
| android.media.audio.common.AudioFormat.PCM_SUB_32_BIT:
|
||||
return AudioFormat.ENCODING_PCM_FLOAT;
|
||||
|
||||
case android.media.audio.common.AudioFormat.AC3:
|
||||
return AudioFormat.ENCODING_AC3;
|
||||
|
||||
case android.media.audio.common.AudioFormat.E_AC3:
|
||||
return AudioFormat.ENCODING_E_AC3;
|
||||
|
||||
case android.media.audio.common.AudioFormat.DTS:
|
||||
return AudioFormat.ENCODING_DTS;
|
||||
|
||||
case android.media.audio.common.AudioFormat.DTS_HD:
|
||||
return AudioFormat.ENCODING_DTS_HD;
|
||||
|
||||
case android.media.audio.common.AudioFormat.MP3:
|
||||
return AudioFormat.ENCODING_MP3;
|
||||
|
||||
case android.media.audio.common.AudioFormat.AAC
|
||||
| android.media.audio.common.AudioFormat.AAC_SUB_LC:
|
||||
return AudioFormat.ENCODING_AAC_LC;
|
||||
|
||||
case android.media.audio.common.AudioFormat.AAC
|
||||
| android.media.audio.common.AudioFormat.AAC_SUB_HE_V1:
|
||||
return AudioFormat.ENCODING_AAC_HE_V1;
|
||||
|
||||
case android.media.audio.common.AudioFormat.AAC
|
||||
| android.media.audio.common.AudioFormat.AAC_SUB_HE_V2:
|
||||
return AudioFormat.ENCODING_AAC_HE_V2;
|
||||
|
||||
case android.media.audio.common.AudioFormat.IEC61937:
|
||||
return AudioFormat.ENCODING_IEC61937;
|
||||
|
||||
case android.media.audio.common.AudioFormat.DOLBY_TRUEHD:
|
||||
return AudioFormat.ENCODING_DOLBY_TRUEHD;
|
||||
|
||||
case android.media.audio.common.AudioFormat.AAC
|
||||
| android.media.audio.common.AudioFormat.AAC_SUB_ELD:
|
||||
return AudioFormat.ENCODING_AAC_ELD;
|
||||
|
||||
case android.media.audio.common.AudioFormat.AAC
|
||||
| android.media.audio.common.AudioFormat.AAC_SUB_XHE:
|
||||
return AudioFormat.ENCODING_AAC_XHE;
|
||||
|
||||
case android.media.audio.common.AudioFormat.AC4:
|
||||
return AudioFormat.ENCODING_AC4;
|
||||
|
||||
case android.media.audio.common.AudioFormat.E_AC3
|
||||
| android.media.audio.common.AudioFormat.E_AC3_SUB_JOC:
|
||||
return AudioFormat.ENCODING_E_AC3_JOC;
|
||||
|
||||
case android.media.audio.common.AudioFormat.MAT:
|
||||
case android.media.audio.common.AudioFormat.MAT
|
||||
| android.media.audio.common.AudioFormat.MAT_SUB_1_0:
|
||||
case android.media.audio.common.AudioFormat.MAT
|
||||
| android.media.audio.common.AudioFormat.MAT_SUB_2_0:
|
||||
case android.media.audio.common.AudioFormat.MAT
|
||||
| android.media.audio.common.AudioFormat.MAT_SUB_2_1:
|
||||
return AudioFormat.ENCODING_DOLBY_MAT;
|
||||
|
||||
case android.media.audio.common.AudioFormat.DEFAULT:
|
||||
return AudioFormat.ENCODING_DEFAULT;
|
||||
|
||||
default:
|
||||
return AudioFormat.ENCODING_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
public static int api2aidlModelParameter(int apiParam) {
|
||||
switch (apiParam) {
|
||||
case ModelParams.THRESHOLD_FACTOR:
|
||||
return android.media.soundtrigger_middleware.ModelParameter.THRESHOLD_FACTOR;
|
||||
default:
|
||||
return android.media.soundtrigger_middleware.ModelParameter.INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
public static int aidl2apiChannelInMask(int aidlMask) {
|
||||
// We're assuming AudioFormat.CHANNEL_IN_* constants are kept in sync with
|
||||
// android.media.audio.common.AudioChannelMask.
|
||||
return aidlMask;
|
||||
}
|
||||
|
||||
public static SoundTrigger.ModelParamRange aidl2apiModelParameterRange(
|
||||
@Nullable ModelParameterRange aidlRange) {
|
||||
if (aidlRange == null) {
|
||||
return null;
|
||||
}
|
||||
return new SoundTrigger.ModelParamRange(aidlRange.minInclusive, aidlRange.maxInclusive);
|
||||
}
|
||||
}
|
||||
@@ -22,18 +22,29 @@ import static android.system.OsConstants.ENOSYS;
|
||||
import static android.system.OsConstants.EPERM;
|
||||
import static android.system.OsConstants.EPIPE;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.SystemApi;
|
||||
import android.annotation.UnsupportedAppUsage;
|
||||
import android.app.ActivityThread;
|
||||
import android.content.Context;
|
||||
import android.media.AudioFormat;
|
||||
import android.media.soundtrigger_middleware.ISoundTriggerMiddlewareService;
|
||||
import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
@@ -44,6 +55,7 @@ import java.util.UUID;
|
||||
*/
|
||||
@SystemApi
|
||||
public class SoundTrigger {
|
||||
private static final String TAG = "SoundTrigger";
|
||||
|
||||
private SoundTrigger() {
|
||||
}
|
||||
@@ -119,15 +131,15 @@ public class SoundTrigger {
|
||||
* recognition callback event */
|
||||
public final boolean returnsTriggerInEvent;
|
||||
|
||||
ModuleProperties(int id, String implementor, String description,
|
||||
String uuid, int version, int maxSoundModels, int maxKeyphrases,
|
||||
ModuleProperties(int id, @NonNull String implementor, @NonNull String description,
|
||||
@NonNull String uuid, int version, int maxSoundModels, int maxKeyphrases,
|
||||
int maxUsers, int recognitionModes, boolean supportsCaptureTransition,
|
||||
int maxBufferMs, boolean supportsConcurrentCapture,
|
||||
int powerConsumptionMw, boolean returnsTriggerInEvent) {
|
||||
this.id = id;
|
||||
this.implementor = implementor;
|
||||
this.description = description;
|
||||
this.uuid = UUID.fromString(uuid);
|
||||
this.implementor = requireNonNull(implementor);
|
||||
this.description = requireNonNull(description);
|
||||
this.uuid = UUID.fromString(requireNonNull(uuid));
|
||||
this.version = version;
|
||||
this.maxSoundModels = maxSoundModels;
|
||||
this.maxKeyphrases = maxKeyphrases;
|
||||
@@ -231,6 +243,7 @@ public class SoundTrigger {
|
||||
|
||||
/** Unique sound model identifier */
|
||||
@UnsupportedAppUsage
|
||||
@NonNull
|
||||
public final UUID uuid;
|
||||
|
||||
/** Sound model type (e.g. TYPE_KEYPHRASE); */
|
||||
@@ -238,17 +251,20 @@ public class SoundTrigger {
|
||||
|
||||
/** Unique sound model vendor identifier */
|
||||
@UnsupportedAppUsage
|
||||
@NonNull
|
||||
public final UUID vendorUuid;
|
||||
|
||||
/** Opaque data. For use by vendor implementation and enrollment application */
|
||||
@UnsupportedAppUsage
|
||||
@NonNull
|
||||
public final byte[] data;
|
||||
|
||||
public SoundModel(UUID uuid, UUID vendorUuid, int type, byte[] data) {
|
||||
this.uuid = uuid;
|
||||
this.vendorUuid = vendorUuid;
|
||||
public SoundModel(@NonNull UUID uuid, @Nullable UUID vendorUuid, int type,
|
||||
@Nullable byte[] data) {
|
||||
this.uuid = requireNonNull(uuid);
|
||||
this.vendorUuid = vendorUuid != null ? vendorUuid : new UUID(0, 0);
|
||||
this.type = type;
|
||||
this.data = data;
|
||||
this.data = data != null ? data : new byte[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -271,8 +287,6 @@ public class SoundTrigger {
|
||||
if (!(obj instanceof SoundModel))
|
||||
return false;
|
||||
SoundModel other = (SoundModel) obj;
|
||||
if (!Arrays.equals(data, other.data))
|
||||
return false;
|
||||
if (type != other.type)
|
||||
return false;
|
||||
if (uuid == null) {
|
||||
@@ -285,6 +299,8 @@ public class SoundTrigger {
|
||||
return false;
|
||||
} else if (!vendorUuid.equals(other.vendorUuid))
|
||||
return false;
|
||||
if (!Arrays.equals(data, other.data))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -306,24 +322,28 @@ public class SoundTrigger {
|
||||
|
||||
/** Locale of the keyphrase. JAVA Locale string e.g en_US */
|
||||
@UnsupportedAppUsage
|
||||
@NonNull
|
||||
public final String locale;
|
||||
|
||||
/** Key phrase text */
|
||||
@UnsupportedAppUsage
|
||||
@NonNull
|
||||
public final String text;
|
||||
|
||||
/** Users this key phrase has been trained for. countains sound trigger specific user IDs
|
||||
* derived from system user IDs {@link android.os.UserHandle#getIdentifier()}. */
|
||||
@UnsupportedAppUsage
|
||||
@NonNull
|
||||
public final int[] users;
|
||||
|
||||
@UnsupportedAppUsage
|
||||
public Keyphrase(int id, int recognitionModes, String locale, String text, int[] users) {
|
||||
public Keyphrase(int id, int recognitionModes, @NonNull String locale, @NonNull String text,
|
||||
@Nullable int[] users) {
|
||||
this.id = id;
|
||||
this.recognitionModes = recognitionModes;
|
||||
this.locale = locale;
|
||||
this.text = text;
|
||||
this.users = users;
|
||||
this.locale = requireNonNull(locale);
|
||||
this.text = requireNonNull(text);
|
||||
this.users = users != null ? users : new int[0];
|
||||
}
|
||||
|
||||
public static final @android.annotation.NonNull Parcelable.Creator<Keyphrase> CREATOR
|
||||
@@ -427,13 +447,15 @@ public class SoundTrigger {
|
||||
public static class KeyphraseSoundModel extends SoundModel implements Parcelable {
|
||||
/** Key phrases in this sound model */
|
||||
@UnsupportedAppUsage
|
||||
@NonNull
|
||||
public final Keyphrase[] keyphrases; // keyword phrases in model
|
||||
|
||||
@UnsupportedAppUsage
|
||||
public KeyphraseSoundModel(
|
||||
UUID uuid, UUID vendorUuid, byte[] data, Keyphrase[] keyphrases) {
|
||||
@NonNull UUID uuid, @NonNull UUID vendorUuid, @Nullable byte[] data,
|
||||
@Nullable Keyphrase[] keyphrases) {
|
||||
super(uuid, vendorUuid, TYPE_KEYPHRASE, data);
|
||||
this.keyphrases = keyphrases;
|
||||
this.keyphrases = keyphrases != null ? keyphrases : new Keyphrase[0];
|
||||
}
|
||||
|
||||
public static final @android.annotation.NonNull Parcelable.Creator<KeyphraseSoundModel> CREATOR
|
||||
@@ -528,7 +550,8 @@ public class SoundTrigger {
|
||||
};
|
||||
|
||||
@UnsupportedAppUsage
|
||||
public GenericSoundModel(UUID uuid, UUID vendorUuid, byte[] data) {
|
||||
public GenericSoundModel(@NonNull UUID uuid, @NonNull UUID vendorUuid,
|
||||
@Nullable byte[] data) {
|
||||
super(uuid, vendorUuid, TYPE_GENERIC_SOUND, data);
|
||||
}
|
||||
|
||||
@@ -648,6 +671,12 @@ public class SoundTrigger {
|
||||
* @hide
|
||||
*/
|
||||
public static final int RECOGNITION_MODE_USER_AUTHENTICATION = 0x4;
|
||||
/**
|
||||
* Generic (non-speech) recognition.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public static final int RECOGNITION_MODE_GENERIC = 0x8;
|
||||
|
||||
/**
|
||||
* Status codes for {@link RecognitionEvent}
|
||||
@@ -739,6 +768,7 @@ public class SoundTrigger {
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@NonNull
|
||||
public final AudioFormat captureFormat;
|
||||
/**
|
||||
* Opaque data for use by system applications who know about voice engine internals,
|
||||
@@ -747,13 +777,14 @@ public class SoundTrigger {
|
||||
* @hide
|
||||
*/
|
||||
@UnsupportedAppUsage
|
||||
@NonNull
|
||||
public final byte[] data;
|
||||
|
||||
/** @hide */
|
||||
@UnsupportedAppUsage
|
||||
public RecognitionEvent(int status, int soundModelHandle, boolean captureAvailable,
|
||||
int captureSession, int captureDelayMs, int capturePreambleMs,
|
||||
boolean triggerInData, AudioFormat captureFormat, byte[] data) {
|
||||
boolean triggerInData, @NonNull AudioFormat captureFormat, @Nullable byte[] data) {
|
||||
this.status = status;
|
||||
this.soundModelHandle = soundModelHandle;
|
||||
this.captureAvailable = captureAvailable;
|
||||
@@ -761,8 +792,8 @@ public class SoundTrigger {
|
||||
this.captureDelayMs = captureDelayMs;
|
||||
this.capturePreambleMs = capturePreambleMs;
|
||||
this.triggerInData = triggerInData;
|
||||
this.captureFormat = captureFormat;
|
||||
this.data = data;
|
||||
this.captureFormat = requireNonNull(captureFormat);
|
||||
this.data = data != null ? data : new byte[0];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -965,19 +996,21 @@ public class SoundTrigger {
|
||||
/** List of all keyphrases in the sound model for which recognition should be performed with
|
||||
* options for each keyphrase. */
|
||||
@UnsupportedAppUsage
|
||||
@NonNull
|
||||
public final KeyphraseRecognitionExtra keyphrases[];
|
||||
/** Opaque data for use by system applications who know about voice engine internals,
|
||||
* typically during enrollment. */
|
||||
@UnsupportedAppUsage
|
||||
@NonNull
|
||||
public final byte[] data;
|
||||
|
||||
@UnsupportedAppUsage
|
||||
public RecognitionConfig(boolean captureRequested, boolean allowMultipleTriggers,
|
||||
KeyphraseRecognitionExtra[] keyphrases, byte[] data) {
|
||||
@Nullable KeyphraseRecognitionExtra[] keyphrases, @Nullable byte[] data) {
|
||||
this.captureRequested = captureRequested;
|
||||
this.allowMultipleTriggers = allowMultipleTriggers;
|
||||
this.keyphrases = keyphrases;
|
||||
this.data = data;
|
||||
this.keyphrases = keyphrases != null ? keyphrases : new KeyphraseRecognitionExtra[0];
|
||||
this.data = data != null ? data : new byte[0];
|
||||
}
|
||||
|
||||
public static final @android.annotation.NonNull Parcelable.Creator<RecognitionConfig> CREATOR
|
||||
@@ -1126,15 +1159,17 @@ public class SoundTrigger {
|
||||
/** Confidence levels for all users recognized (KeyphraseRecognitionEvent) or to
|
||||
* be recognized (RecognitionConfig) */
|
||||
@UnsupportedAppUsage
|
||||
@NonNull
|
||||
public final ConfidenceLevel[] confidenceLevels;
|
||||
|
||||
@UnsupportedAppUsage
|
||||
public KeyphraseRecognitionExtra(int id, int recognitionModes, int coarseConfidenceLevel,
|
||||
ConfidenceLevel[] confidenceLevels) {
|
||||
@Nullable ConfidenceLevel[] confidenceLevels) {
|
||||
this.id = id;
|
||||
this.recognitionModes = recognitionModes;
|
||||
this.coarseConfidenceLevel = coarseConfidenceLevel;
|
||||
this.confidenceLevels = confidenceLevels;
|
||||
this.confidenceLevels =
|
||||
confidenceLevels != null ? confidenceLevels : new ConfidenceLevel[0];
|
||||
}
|
||||
|
||||
public static final @android.annotation.NonNull Parcelable.Creator<KeyphraseRecognitionExtra> CREATOR
|
||||
@@ -1217,16 +1252,18 @@ public class SoundTrigger {
|
||||
public static class KeyphraseRecognitionEvent extends RecognitionEvent implements Parcelable {
|
||||
/** Indicates if the key phrase is present in the buffered audio available for capture */
|
||||
@UnsupportedAppUsage
|
||||
@NonNull
|
||||
public final KeyphraseRecognitionExtra[] keyphraseExtras;
|
||||
|
||||
@UnsupportedAppUsage
|
||||
public KeyphraseRecognitionEvent(int status, int soundModelHandle, boolean captureAvailable,
|
||||
int captureSession, int captureDelayMs, int capturePreambleMs,
|
||||
boolean triggerInData, AudioFormat captureFormat, byte[] data,
|
||||
KeyphraseRecognitionExtra[] keyphraseExtras) {
|
||||
boolean triggerInData, @NonNull AudioFormat captureFormat, @Nullable byte[] data,
|
||||
@Nullable KeyphraseRecognitionExtra[] keyphraseExtras) {
|
||||
super(status, soundModelHandle, captureAvailable, captureSession, captureDelayMs,
|
||||
capturePreambleMs, triggerInData, captureFormat, data);
|
||||
this.keyphraseExtras = keyphraseExtras;
|
||||
this.keyphraseExtras =
|
||||
keyphraseExtras != null ? keyphraseExtras : new KeyphraseRecognitionExtra[0];
|
||||
}
|
||||
|
||||
public static final @android.annotation.NonNull Parcelable.Creator<KeyphraseRecognitionEvent> CREATOR
|
||||
@@ -1343,8 +1380,8 @@ public class SoundTrigger {
|
||||
@UnsupportedAppUsage
|
||||
public GenericRecognitionEvent(int status, int soundModelHandle,
|
||||
boolean captureAvailable, int captureSession, int captureDelayMs,
|
||||
int capturePreambleMs, boolean triggerInData, AudioFormat captureFormat,
|
||||
byte[] data) {
|
||||
int capturePreambleMs, boolean triggerInData, @NonNull AudioFormat captureFormat,
|
||||
@Nullable byte[] data) {
|
||||
super(status, soundModelHandle, captureAvailable, captureSession,
|
||||
captureDelayMs, capturePreambleMs, triggerInData, captureFormat,
|
||||
data);
|
||||
@@ -1408,13 +1445,14 @@ public class SoundTrigger {
|
||||
/** The updated sound model handle */
|
||||
public final int soundModelHandle;
|
||||
/** New sound model data */
|
||||
@NonNull
|
||||
public final byte[] data;
|
||||
|
||||
@UnsupportedAppUsage
|
||||
SoundModelEvent(int status, int soundModelHandle, byte[] data) {
|
||||
SoundModelEvent(int status, int soundModelHandle, @Nullable byte[] data) {
|
||||
this.status = status;
|
||||
this.soundModelHandle = soundModelHandle;
|
||||
this.data = data;
|
||||
this.data = data != null ? data : new byte[0];
|
||||
}
|
||||
|
||||
public static final @android.annotation.NonNull Parcelable.Creator<SoundModelEvent> CREATOR
|
||||
@@ -1498,8 +1536,9 @@ public class SoundTrigger {
|
||||
* @hide
|
||||
*/
|
||||
public static final int SERVICE_STATE_DISABLED = 1;
|
||||
|
||||
/**
|
||||
private static Object mServiceLock = new Object();
|
||||
private static ISoundTriggerMiddlewareService mService;
|
||||
/**
|
||||
* @return returns current package name.
|
||||
*/
|
||||
static String getCurrentOpPackageName() {
|
||||
@@ -1523,24 +1562,21 @@ public class SoundTrigger {
|
||||
* @hide
|
||||
*/
|
||||
@UnsupportedAppUsage
|
||||
public static int listModules(ArrayList<ModuleProperties> modules) {
|
||||
return listModules(getCurrentOpPackageName(), modules);
|
||||
public static int listModules(@NonNull ArrayList<ModuleProperties> modules) {
|
||||
try {
|
||||
SoundTriggerModuleDescriptor[] descs = getService().listModules();
|
||||
modules.clear();
|
||||
modules.ensureCapacity(descs.length);
|
||||
for (SoundTriggerModuleDescriptor desc : descs) {
|
||||
modules.add(ConversionUtil.aidl2apiModuleDescriptor(desc));
|
||||
}
|
||||
return STATUS_OK;
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Exception caught", e);
|
||||
return STATUS_DEAD_OBJECT;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of descriptors for all hardware modules loaded.
|
||||
* @param opPackageName
|
||||
* @param modules A ModuleProperties array where the list will be returned.
|
||||
* @return - {@link #STATUS_OK} in case of success
|
||||
* - {@link #STATUS_ERROR} in case of unspecified error
|
||||
* - {@link #STATUS_PERMISSION_DENIED} if the caller does not have system permission
|
||||
* - {@link #STATUS_NO_INIT} if the native service cannot be reached
|
||||
* - {@link #STATUS_BAD_VALUE} if modules is null
|
||||
* - {@link #STATUS_DEAD_OBJECT} if the binder transaction to the native service fails
|
||||
*/
|
||||
private static native int listModules(String opPackageName,
|
||||
ArrayList<ModuleProperties> modules);
|
||||
|
||||
/**
|
||||
* Get an interface on a hardware module to control sound models and recognition on
|
||||
* this module.
|
||||
@@ -1553,14 +1589,40 @@ public class SoundTrigger {
|
||||
* @hide
|
||||
*/
|
||||
@UnsupportedAppUsage
|
||||
public static SoundTriggerModule attachModule(int moduleId,
|
||||
StatusListener listener,
|
||||
Handler handler) {
|
||||
if (listener == null) {
|
||||
public static @NonNull SoundTriggerModule attachModule(int moduleId,
|
||||
@NonNull StatusListener listener,
|
||||
@Nullable Handler handler) {
|
||||
Looper looper = handler != null ? handler.getLooper() : Looper.getMainLooper();
|
||||
try {
|
||||
return new SoundTriggerModule(getService(), moduleId, listener, looper);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "", e);
|
||||
return null;
|
||||
}
|
||||
SoundTriggerModule module = new SoundTriggerModule(moduleId, listener, handler);
|
||||
return module;
|
||||
}
|
||||
|
||||
private static ISoundTriggerMiddlewareService getService() {
|
||||
synchronized (mServiceLock) {
|
||||
while (true) {
|
||||
IBinder binder = null;
|
||||
try {
|
||||
binder =
|
||||
ServiceManager.getServiceOrThrow(
|
||||
Context.SOUND_TRIGGER_MIDDLEWARE_SERVICE);
|
||||
binder.linkToDeath(() -> {
|
||||
synchronized (mServiceLock) {
|
||||
mService = null;
|
||||
}
|
||||
}, 0);
|
||||
mService = ISoundTriggerMiddlewareService.Stub.asInterface(binder);
|
||||
break;
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Failed to bind to soundtrigger service", e);
|
||||
}
|
||||
}
|
||||
return mService;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -16,14 +16,23 @@
|
||||
|
||||
package android.hardware.soundtrigger;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.UnsupportedAppUsage;
|
||||
import android.hardware.soundtrigger.SoundTrigger.ModelParamRange;
|
||||
import android.media.soundtrigger_middleware.ISoundTriggerCallback;
|
||||
import android.media.soundtrigger_middleware.ISoundTriggerMiddlewareService;
|
||||
import android.media.soundtrigger_middleware.ISoundTriggerModule;
|
||||
import android.media.soundtrigger_middleware.ModelParameterRange;
|
||||
import android.media.soundtrigger_middleware.PhraseRecognitionEvent;
|
||||
import android.media.soundtrigger_middleware.PhraseSoundModel;
|
||||
import android.media.soundtrigger_middleware.RecognitionEvent;
|
||||
import android.media.soundtrigger_middleware.SoundModel;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* The SoundTriggerModule provides APIs to control sound models and sound detection
|
||||
@@ -32,39 +41,47 @@ import java.lang.ref.WeakReference;
|
||||
* @hide
|
||||
*/
|
||||
public class SoundTriggerModule {
|
||||
@UnsupportedAppUsage
|
||||
private long mNativeContext;
|
||||
private static final String TAG = "SoundTriggerModule";
|
||||
|
||||
@UnsupportedAppUsage
|
||||
private int mId;
|
||||
private NativeEventHandlerDelegate mEventHandlerDelegate;
|
||||
|
||||
// to be kept in sync with core/jni/android_hardware_SoundTrigger.cpp
|
||||
private static final int EVENT_RECOGNITION = 1;
|
||||
private static final int EVENT_SERVICE_DIED = 2;
|
||||
private static final int EVENT_SOUNDMODEL = 3;
|
||||
private static final int EVENT_SERVICE_STATE_CHANGE = 4;
|
||||
private static final int EVENT_SERVICE_STATE_CHANGE = 3;
|
||||
@UnsupportedAppUsage
|
||||
private int mId;
|
||||
private EventHandlerDelegate mEventHandlerDelegate;
|
||||
private ISoundTriggerModule mService;
|
||||
|
||||
SoundTriggerModule(int moduleId, SoundTrigger.StatusListener listener, Handler handler) {
|
||||
SoundTriggerModule(@NonNull ISoundTriggerMiddlewareService service,
|
||||
int moduleId, @NonNull SoundTrigger.StatusListener listener, @NonNull Looper looper)
|
||||
throws RemoteException {
|
||||
mId = moduleId;
|
||||
mEventHandlerDelegate = new NativeEventHandlerDelegate(listener, handler);
|
||||
native_setup(SoundTrigger.getCurrentOpPackageName(),
|
||||
new WeakReference<SoundTriggerModule>(this));
|
||||
mEventHandlerDelegate = new EventHandlerDelegate(listener, looper);
|
||||
mService = service.attach(moduleId, mEventHandlerDelegate);
|
||||
mService.asBinder().linkToDeath(mEventHandlerDelegate, 0);
|
||||
}
|
||||
private native void native_setup(String opPackageName, Object moduleThis);
|
||||
|
||||
@Override
|
||||
protected void finalize() {
|
||||
native_finalize();
|
||||
detach();
|
||||
}
|
||||
private native void native_finalize();
|
||||
|
||||
/**
|
||||
* Detach from this module. The {@link SoundTrigger.StatusListener} callback will not be called
|
||||
* anymore and associated resources will be released.
|
||||
* */
|
||||
* All models must have been unloaded prior to detaching.
|
||||
*/
|
||||
@UnsupportedAppUsage
|
||||
public native void detach();
|
||||
public synchronized void detach() {
|
||||
try {
|
||||
if (mService != null) {
|
||||
mService.asBinder().unlinkToDeath(mEventHandlerDelegate, 0);
|
||||
mService.detach();
|
||||
mService = null;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
handleException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a {@link SoundTrigger.SoundModel} to the hardware. A sound model must be loaded in
|
||||
@@ -82,7 +99,26 @@ public class SoundTriggerModule {
|
||||
* - {@link SoundTrigger#STATUS_INVALID_OPERATION} if the call is out of sequence
|
||||
*/
|
||||
@UnsupportedAppUsage
|
||||
public native int loadSoundModel(SoundTrigger.SoundModel model, int[] soundModelHandle);
|
||||
public synchronized int loadSoundModel(@NonNull SoundTrigger.SoundModel model,
|
||||
@NonNull int[] soundModelHandle) {
|
||||
try {
|
||||
if (model instanceof SoundTrigger.GenericSoundModel) {
|
||||
SoundModel aidlModel = ConversionUtil.api2aidlGenericSoundModel(
|
||||
(SoundTrigger.GenericSoundModel) model);
|
||||
soundModelHandle[0] = mService.loadModel(aidlModel);
|
||||
return SoundTrigger.STATUS_OK;
|
||||
}
|
||||
if (model instanceof SoundTrigger.KeyphraseSoundModel) {
|
||||
PhraseSoundModel aidlModel = ConversionUtil.api2aidlPhraseSoundModel(
|
||||
(SoundTrigger.KeyphraseSoundModel) model);
|
||||
soundModelHandle[0] = mService.loadPhraseModel(aidlModel);
|
||||
return SoundTrigger.STATUS_OK;
|
||||
}
|
||||
return SoundTrigger.STATUS_BAD_VALUE;
|
||||
} catch (Exception e) {
|
||||
return handleException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unload a {@link SoundTrigger.SoundModel} and abort any pendiong recognition
|
||||
@@ -97,7 +133,14 @@ public class SoundTriggerModule {
|
||||
* service fails
|
||||
*/
|
||||
@UnsupportedAppUsage
|
||||
public native int unloadSoundModel(int soundModelHandle);
|
||||
public synchronized int unloadSoundModel(int soundModelHandle) {
|
||||
try {
|
||||
mService.unloadModel(soundModelHandle);
|
||||
return SoundTrigger.STATUS_OK;
|
||||
} catch (Exception e) {
|
||||
return handleException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start listening to all key phrases in a {@link SoundTrigger.SoundModel}.
|
||||
@@ -117,7 +160,16 @@ public class SoundTriggerModule {
|
||||
* - {@link SoundTrigger#STATUS_INVALID_OPERATION} if the call is out of sequence
|
||||
*/
|
||||
@UnsupportedAppUsage
|
||||
public native int startRecognition(int soundModelHandle, SoundTrigger.RecognitionConfig config);
|
||||
public synchronized int startRecognition(int soundModelHandle,
|
||||
SoundTrigger.RecognitionConfig config) {
|
||||
try {
|
||||
mService.startRecognition(soundModelHandle,
|
||||
ConversionUtil.api2aidlRecognitionConfig(config));
|
||||
return SoundTrigger.STATUS_OK;
|
||||
} catch (Exception e) {
|
||||
return handleException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop listening to all key phrases in a {@link SoundTrigger.SoundModel}
|
||||
@@ -133,12 +185,20 @@ public class SoundTriggerModule {
|
||||
* - {@link SoundTrigger#STATUS_INVALID_OPERATION} if the call is out of sequence
|
||||
*/
|
||||
@UnsupportedAppUsage
|
||||
public native int stopRecognition(int soundModelHandle);
|
||||
public synchronized int stopRecognition(int soundModelHandle) {
|
||||
try {
|
||||
mService.stopRecognition(soundModelHandle);
|
||||
return SoundTrigger.STATUS_OK;
|
||||
} catch (Exception e) {
|
||||
return handleException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current state of a {@link SoundTrigger.SoundModel}.
|
||||
* The state will be returned asynchronously as a {@link SoundTrigger#RecognitionEvent}
|
||||
* in the callback registered in the {@link SoundTrigger.startRecognition} method.
|
||||
* The state will be returned asynchronously as a {@link SoundTrigger.RecognitionEvent}
|
||||
* in the callback registered in the
|
||||
* {@link SoundTrigger#attachModule(int, SoundTrigger.StatusListener, Handler)} method.
|
||||
* @param soundModelHandle The sound model handle indicating which model's state to return
|
||||
* @return - {@link SoundTrigger#STATUS_OK} in case of success
|
||||
* - {@link SoundTrigger#STATUS_ERROR} in case of unspecified error
|
||||
@@ -150,46 +210,71 @@ public class SoundTriggerModule {
|
||||
* service fails
|
||||
* - {@link SoundTrigger#STATUS_INVALID_OPERATION} if the call is out of sequence
|
||||
*/
|
||||
public native int getModelState(int soundModelHandle);
|
||||
public synchronized int getModelState(int soundModelHandle) {
|
||||
try {
|
||||
mService.forceRecognitionEvent(soundModelHandle);
|
||||
return SoundTrigger.STATUS_OK;
|
||||
} catch (Exception e) {
|
||||
return handleException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a model specific {@link ModelParams} with the given value. This
|
||||
* parameter will keep its value for the duration the model is loaded regardless of starting and
|
||||
* parameter will keep its value for the duration the model is loaded regardless of starting
|
||||
* and
|
||||
* stopping recognition. Once the model is unloaded, the value will be lost.
|
||||
* {@link SoundTriggerModule#isParameterSupported} should be checked first before calling this
|
||||
* method.
|
||||
* {@link SoundTriggerModule#queryParameter(int, int)} should be checked first before calling
|
||||
* this method.
|
||||
*
|
||||
* @param soundModelHandle handle of model to apply parameter
|
||||
* @param modelParam {@link ModelParams}
|
||||
* @param value Value to set
|
||||
* @param modelParam {@link ModelParams}
|
||||
* @param value Value to set
|
||||
* @return - {@link SoundTrigger#STATUS_OK} in case of success
|
||||
* - {@link SoundTrigger#STATUS_NO_INIT} if the native service cannot be reached
|
||||
* - {@link SoundTrigger#STATUS_BAD_VALUE} invalid input parameter
|
||||
* - {@link SoundTrigger#STATUS_INVALID_OPERATION} if the call is out of sequence or
|
||||
* if API is not supported by HAL
|
||||
* - {@link SoundTrigger#STATUS_NO_INIT} if the native service cannot be reached
|
||||
* - {@link SoundTrigger#STATUS_BAD_VALUE} invalid input parameter
|
||||
* - {@link SoundTrigger#STATUS_INVALID_OPERATION} if the call is out of sequence or
|
||||
* if API is not supported by HAL
|
||||
*/
|
||||
public native int setParameter(int soundModelHandle,
|
||||
@ModelParams int modelParam, int value);
|
||||
public synchronized int setParameter(int soundModelHandle, @ModelParams int modelParam,
|
||||
int value) {
|
||||
try {
|
||||
mService.setModelParameter(soundModelHandle,
|
||||
ConversionUtil.api2aidlModelParameter(modelParam), value);
|
||||
return SoundTrigger.STATUS_OK;
|
||||
} catch (Exception e) {
|
||||
return handleException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a model specific {@link ModelParams}. This parameter will keep its value
|
||||
* for the duration the model is loaded regardless of starting and stopping recognition.
|
||||
* Once the model is unloaded, the value will be lost. If the value is not set, a default
|
||||
* value is returned. See {@link ModelParams} for parameter default values.
|
||||
* {@link SoundTriggerModule#isParameterSupported} should be checked first before
|
||||
* {@link SoundTriggerModule#queryParameter(int, int)} should be checked first before
|
||||
* calling this method. Otherwise, an exception can be thrown.
|
||||
*
|
||||
* @param soundModelHandle handle of model to get parameter
|
||||
* @param modelParam {@link ModelParams}
|
||||
* @param modelParam {@link ModelParams}
|
||||
* @return value of parameter
|
||||
* @throws UnsupportedOperationException if hal or model do not support this API.
|
||||
* {@link SoundTriggerModule#isParameterSupported} should be checked first.
|
||||
* @throws IllegalArgumentException if invalid model handle or parameter is passed.
|
||||
* {@link SoundTriggerModule#isParameterSupported} should be checked first.
|
||||
* {@link SoundTriggerModule#queryParameter(int, int)}
|
||||
* should
|
||||
* be checked first.
|
||||
* @throws IllegalArgumentException if invalid model handle or parameter is passed.
|
||||
* {@link SoundTriggerModule#queryParameter(int, int)}
|
||||
* should be checked first.
|
||||
*/
|
||||
public native int getParameter(int soundModelHandle,
|
||||
@ModelParams int modelParam)
|
||||
throws UnsupportedOperationException, IllegalArgumentException;
|
||||
public synchronized int getParameter(int soundModelHandle, @ModelParams int modelParam)
|
||||
throws UnsupportedOperationException, IllegalArgumentException {
|
||||
try {
|
||||
return mService.getModelParameter(soundModelHandle,
|
||||
ConversionUtil.api2aidlModelParameter(modelParam));
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if parameter control is supported for the given model handle.
|
||||
@@ -197,85 +282,98 @@ public class SoundTriggerModule {
|
||||
* {@link SoundTriggerModule#getParameter}.
|
||||
*
|
||||
* @param soundModelHandle handle of model to get parameter
|
||||
* @param modelParam {@link ModelParams}
|
||||
* @param modelParam {@link ModelParams}
|
||||
* @return supported range of parameter, null if not supported
|
||||
*/
|
||||
@Nullable
|
||||
public native ModelParamRange queryParameter(int soundModelHandle, @ModelParams int modelParam);
|
||||
|
||||
private class NativeEventHandlerDelegate {
|
||||
private final Handler mHandler;
|
||||
|
||||
NativeEventHandlerDelegate(final SoundTrigger.StatusListener listener,
|
||||
Handler handler) {
|
||||
// find the looper for our new event handler
|
||||
Looper looper;
|
||||
if (handler != null) {
|
||||
looper = handler.getLooper();
|
||||
} else {
|
||||
looper = Looper.getMainLooper();
|
||||
}
|
||||
|
||||
// construct the event handler with this looper
|
||||
if (looper != null) {
|
||||
// implement the event handler delegate
|
||||
mHandler = new Handler(looper) {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
switch(msg.what) {
|
||||
case EVENT_RECOGNITION:
|
||||
if (listener != null) {
|
||||
listener.onRecognition(
|
||||
(SoundTrigger.RecognitionEvent)msg.obj);
|
||||
}
|
||||
break;
|
||||
case EVENT_SOUNDMODEL:
|
||||
if (listener != null) {
|
||||
listener.onSoundModelUpdate(
|
||||
(SoundTrigger.SoundModelEvent)msg.obj);
|
||||
}
|
||||
break;
|
||||
case EVENT_SERVICE_STATE_CHANGE:
|
||||
if (listener != null) {
|
||||
listener.onServiceStateChange(msg.arg1);
|
||||
}
|
||||
break;
|
||||
case EVENT_SERVICE_DIED:
|
||||
if (listener != null) {
|
||||
listener.onServiceDied();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
} else {
|
||||
mHandler = null;
|
||||
}
|
||||
}
|
||||
|
||||
Handler handler() {
|
||||
return mHandler;
|
||||
public synchronized SoundTrigger.ModelParamRange queryParameter(int soundModelHandle,
|
||||
@ModelParams int modelParam) {
|
||||
try {
|
||||
return ConversionUtil.aidl2apiModelParameterRange(mService.queryModelParameterSupport(
|
||||
soundModelHandle,
|
||||
ConversionUtil.api2aidlModelParameter(modelParam)));
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@UnsupportedAppUsage
|
||||
private static void postEventFromNative(Object module_ref,
|
||||
int what, int arg1, int arg2, Object obj) {
|
||||
SoundTriggerModule module = (SoundTriggerModule)((WeakReference)module_ref).get();
|
||||
if (module == null) {
|
||||
return;
|
||||
private int handleException(Exception e) {
|
||||
Log.e(TAG, "", e);
|
||||
if (e instanceof NullPointerException) {
|
||||
return SoundTrigger.STATUS_NO_INIT;
|
||||
}
|
||||
if (e instanceof RemoteException) {
|
||||
return SoundTrigger.STATUS_DEAD_OBJECT;
|
||||
}
|
||||
if (e instanceof IllegalArgumentException) {
|
||||
return SoundTrigger.STATUS_BAD_VALUE;
|
||||
}
|
||||
if (e instanceof IllegalStateException) {
|
||||
return SoundTrigger.STATUS_INVALID_OPERATION;
|
||||
}
|
||||
return SoundTrigger.STATUS_ERROR;
|
||||
}
|
||||
|
||||
private class EventHandlerDelegate extends ISoundTriggerCallback.Stub implements
|
||||
IBinder.DeathRecipient {
|
||||
private final Handler mHandler;
|
||||
|
||||
EventHandlerDelegate(@NonNull final SoundTrigger.StatusListener listener,
|
||||
@NonNull Looper looper) {
|
||||
|
||||
// construct the event handler with this looper
|
||||
// implement the event handler delegate
|
||||
mHandler = new Handler(looper) {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case EVENT_RECOGNITION:
|
||||
listener.onRecognition(
|
||||
(SoundTrigger.RecognitionEvent) msg.obj);
|
||||
break;
|
||||
case EVENT_SERVICE_STATE_CHANGE:
|
||||
listener.onServiceStateChange(msg.arg1);
|
||||
break;
|
||||
case EVENT_SERVICE_DIED:
|
||||
listener.onServiceDied();
|
||||
break;
|
||||
default:
|
||||
Log.e(TAG, "Unknown message: " + msg.toString());
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
NativeEventHandlerDelegate delegate = module.mEventHandlerDelegate;
|
||||
if (delegate != null) {
|
||||
Handler handler = delegate.handler();
|
||||
if (handler != null) {
|
||||
Message m = handler.obtainMessage(what, arg1, arg2, obj);
|
||||
handler.sendMessage(m);
|
||||
}
|
||||
@Override
|
||||
public synchronized void onRecognition(int handle, RecognitionEvent event)
|
||||
throws RemoteException {
|
||||
Message m = mHandler.obtainMessage(EVENT_RECOGNITION,
|
||||
ConversionUtil.aidl2apiRecognitionEvent(handle, event));
|
||||
mHandler.sendMessage(m);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void onPhraseRecognition(int handle, PhraseRecognitionEvent event)
|
||||
throws RemoteException {
|
||||
Message m = mHandler.obtainMessage(EVENT_RECOGNITION,
|
||||
ConversionUtil.aidl2apiPhraseRecognitionEvent(handle, event));
|
||||
mHandler.sendMessage(m);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void onRecognitionAvailabilityChange(boolean available)
|
||||
throws RemoteException {
|
||||
Message m = mHandler.obtainMessage(EVENT_SERVICE_STATE_CHANGE,
|
||||
available ? SoundTrigger.SERVICE_STATE_ENABLED
|
||||
: SoundTrigger.SERVICE_STATE_DISABLED);
|
||||
mHandler.sendMessage(m);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void binderDied() {
|
||||
Message m = mHandler.obtainMessage(EVENT_SERVICE_DIED);
|
||||
mHandler.sendMessage(m);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,7 +177,6 @@ cc_library_shared {
|
||||
"android_hardware_HardwareBuffer.cpp",
|
||||
"android_hardware_SensorManager.cpp",
|
||||
"android_hardware_SerialPort.cpp",
|
||||
"android_hardware_SoundTrigger.cpp",
|
||||
"android_hardware_UsbDevice.cpp",
|
||||
"android_hardware_UsbDeviceConnection.cpp",
|
||||
"android_hardware_UsbRequest.cpp",
|
||||
@@ -259,7 +258,6 @@ cc_library_shared {
|
||||
"libpdfium",
|
||||
"libimg_utils",
|
||||
"libnetd_client",
|
||||
"libsoundtrigger",
|
||||
"libprocessgroup",
|
||||
"libnativebridge_lazy",
|
||||
"libnativeloader_lazy",
|
||||
|
||||
@@ -80,7 +80,6 @@ extern int register_android_hardware_camera2_DngCreator(JNIEnv *env);
|
||||
extern int register_android_hardware_HardwareBuffer(JNIEnv *env);
|
||||
extern int register_android_hardware_SensorManager(JNIEnv *env);
|
||||
extern int register_android_hardware_SerialPort(JNIEnv *env);
|
||||
extern int register_android_hardware_SoundTrigger(JNIEnv *env);
|
||||
extern int register_android_hardware_UsbDevice(JNIEnv *env);
|
||||
extern int register_android_hardware_UsbDeviceConnection(JNIEnv *env);
|
||||
extern int register_android_hardware_UsbRequest(JNIEnv *env);
|
||||
@@ -1508,7 +1507,6 @@ static const RegJNIRec gRegJNI[] = {
|
||||
REG_JNI(register_android_hardware_HardwareBuffer),
|
||||
REG_JNI(register_android_hardware_SensorManager),
|
||||
REG_JNI(register_android_hardware_SerialPort),
|
||||
REG_JNI(register_android_hardware_SoundTrigger),
|
||||
REG_JNI(register_android_hardware_UsbDevice),
|
||||
REG_JNI(register_android_hardware_UsbDeviceConnection),
|
||||
REG_JNI(register_android_hardware_UsbRequest),
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -65,7 +65,7 @@ import java.util.Set;
|
||||
* {@link android.media.soundtrigger_middleware.Status} constants. Any other exception
|
||||
* thrown should be regarded as a bug in the implementation or one of its dependencies
|
||||
* (assuming correct usage).
|
||||
* <li>The implementation is designed for testibility by featuring dependency injection (the
|
||||
* <li>The implementation is designed for testability by featuring dependency injection (the
|
||||
* underlying HAL driver instances are passed to the ctor) and by minimizing dependencies
|
||||
* on Android runtime.
|
||||
* <li>The implementation is thread-safe. This is achieved by a simplistic model, where all entry-
|
||||
|
||||
Reference in New Issue
Block a user