SoundTrigger API improvements.

This CL implements the SoundTrigger API improvements as given in b/22860713. Only the java-level
parts are implemented in this CL.

Key changes include:

* Addition of a SoundTriggerManager/SoundTriggerDetector system API to manage
  the sound-trigger based sound models.
* Addition of a SoundTriggerService service that manages all sound models
  including voice (keyphrase) and sound-trigger based models.
* Includes logic to write sound-trigger based models to the database.
* VoiceInteractionManager service now uses SoundTriggerService instead of
  SoundTriggerHelper.

Bug: 22860713
Change-Id: I7b5c0ed80702527c4460372efeb5e542d3693a69
This commit is contained in:
Arunesh Mishra
2016-01-25 10:33:11 -08:00
parent 5c461fc36d
commit a772e5fc06
23 changed files with 1289 additions and 19 deletions

View File

@@ -288,6 +288,7 @@ LOCAL_SRC_FILES += \
core/java/com/android/internal/app/IBatteryStats.aidl \
core/java/com/android/internal/app/IEphemeralResolver.aidl \
core/java/com/android/internal/app/IProcessStats.aidl \
core/java/com/android/internal/app/ISoundTriggerService.aidl \
core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl \
core/java/com/android/internal/app/IVoiceInteractionSessionShowCallback.aidl \
core/java/com/android/internal/app/IVoiceInteractor.aidl \

View File

@@ -17,6 +17,7 @@
package android.app;
import com.android.internal.app.IAppOpsService;
import com.android.internal.app.ISoundTriggerService;
import com.android.internal.appwidget.IAppWidgetService;
import com.android.internal.os.IDropBoxManagerService;
@@ -61,6 +62,7 @@ import android.media.midi.IMidiManager;
import android.media.midi.MidiManager;
import android.media.projection.MediaProjectionManager;
import android.media.session.MediaSessionManager;
import android.media.soundtrigger.SoundTriggerManager;
import android.media.tv.ITvInputManager;
import android.media.tv.TvInputManager;
import android.net.ConnectivityManager;
@@ -708,12 +710,22 @@ final class SystemServiceRegistry {
public RadioManager createService(ContextImpl ctx) {
return new RadioManager(ctx);
}});
registerService(Context.HARDWARE_PROPERTIES_SERVICE, HardwarePropertiesManager.class,
new CachedServiceFetcher<HardwarePropertiesManager>() {
@Override
public HardwarePropertiesManager createService(ContextImpl ctx) {
return new HardwarePropertiesManager();
}});
registerService(Context.SOUND_TRIGGER_SERVICE, SoundTriggerManager.class,
new CachedServiceFetcher<SoundTriggerManager>() {
@Override
public SoundTriggerManager createService(ContextImpl ctx) {
IBinder b = ServiceManager.getService(Context.SOUND_TRIGGER_SERVICE);
Log.i(TAG, "Creating new instance of SoundTriggerManager object.");
return new SoundTriggerManager(ctx, ISoundTriggerService.Stub.asInterface(b));
}});
}
/**

View File

@@ -2613,6 +2613,7 @@ public abstract class Context {
MIDI_SERVICE,
RADIO_SERVICE,
HARDWARE_PROPERTIES_SERVICE,
//@hide: SOUND_TRIGGER_SERVICE,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ServiceName {}
@@ -3194,6 +3195,16 @@ public abstract class Context {
*/
public static final String VOICE_INTERACTION_MANAGER_SERVICE = "voiceinteraction";
/**
* Use with {@link #getSystemService} to access the
* {@link com.android.server.voiceinteraction.SoundTriggerService}.
*
* @hide
* @see #getSystemService
*/
public static final String SOUND_TRIGGER_SERVICE = "soundtrigger";
/**
* Use with {@link #getSystemService} to retrieve an
* {@link android.app.backup.IBackupManager IBackupManager} for communicating

View File

@@ -25,9 +25,12 @@ oneway interface IRecognitionStatusCallback {
/**
* Called when the keyphrase is spoken.
*
* @param data Optional trigger audio data, if it was requested and is available.
* @param recognitionEvent Object containing data relating to the
* recognition event such as trigger audio data, if it was requested
* and is available.
*/
void onDetected(in SoundTrigger.KeyphraseRecognitionEvent recognitionEvent);
void onDetected(in SoundTrigger.RecognitionEvent recognitionEvent);
/**
* Called when the detection fails due to an error.
*
@@ -42,4 +45,4 @@ oneway interface IRecognitionStatusCallback {
* Called when the recognition is resumed after it was temporarily paused.
*/
void onRecognitionResumed();
}
}

View File

@@ -18,8 +18,11 @@ package android.hardware.soundtrigger;
parcelable SoundTrigger.ConfidenceLevel;
parcelable SoundTrigger.Keyphrase;
parcelable SoundTrigger.RecognitionEvent;
parcelable SoundTrigger.KeyphraseRecognitionEvent;
parcelable SoundTrigger.GenericSoundRecognitionEvent;
parcelable SoundTrigger.KeyphraseRecognitionExtra;
parcelable SoundTrigger.KeyphraseSoundModel;
parcelable SoundTrigger.GenericSoundModel;
parcelable SoundTrigger.ModuleProperties;
parcelable SoundTrigger.RecognitionConfig;

View File

@@ -195,6 +195,12 @@ public class SoundTrigger {
/** Keyphrase sound model */
public static final int TYPE_KEYPHRASE = 0;
/**
* A generic sound model. Use this type only for non-keyphrase sound models such as
* ones that match a particular sound pattern.
*/
public static final int TYPE_GENERIC_SOUND = 1;
/** Unique sound model identifier */
public final UUID uuid;
@@ -458,6 +464,63 @@ public class SoundTrigger {
}
}
/*****************************************************************************
* A GenericSoundModel is a specialized {@link SoundModel} for non-voice sound
* patterns.
****************************************************************************/
public static class GenericSoundModel extends SoundModel implements Parcelable {
public static final Parcelable.Creator<GenericSoundModel> CREATOR
= new Parcelable.Creator<GenericSoundModel>() {
public GenericSoundModel createFromParcel(Parcel in) {
return GenericSoundModel.fromParcel(in);
}
public GenericSoundModel[] newArray(int size) {
return new GenericSoundModel[size];
}
};
public GenericSoundModel(UUID uuid, UUID vendorUuid, byte[] data) {
super(uuid, vendorUuid, TYPE_GENERIC_SOUND, data);
}
@Override
public int describeContents() {
return 0;
}
private static GenericSoundModel fromParcel(Parcel in) {
UUID uuid = UUID.fromString(in.readString());
UUID vendorUuid = null;
int length = in.readInt();
if (length >= 0) {
vendorUuid = UUID.fromString(in.readString());
}
byte[] data = in.readBlob();
return new GenericSoundModel(uuid, vendorUuid, data);
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(uuid.toString());
if (vendorUuid == null) {
dest.writeInt(-1);
} else {
dest.writeInt(vendorUuid.toString().length());
dest.writeString(vendorUuid.toString());
}
dest.writeBlob(data);
}
@Override
public String toString() {
return "GenericSoundModel [uuid=" + uuid + ", vendorUuid=" + vendorUuid
+ ", type=" + type + ", data=" + (data == null ? 0 : data.length) + "]";
}
}
/**
* Modes for key phrase recognition
*/
@@ -1018,6 +1081,21 @@ public class SoundTrigger {
}
}
/**
* Sub-class of RecognitionEvent specifically for sound-trigger based sound
* models(non-keyphrase). Currently does not contain any additional fields.
*/
public static class GenericRecognitionEvent extends RecognitionEvent {
public GenericRecognitionEvent(int status, int soundModelHandle,
boolean captureAvailable, int captureSession, int captureDelayMs,
int capturePreambleMs, boolean triggerInData, AudioFormat captureFormat,
byte[] data) {
super(status, soundModelHandle, captureAvailable, captureSession,
captureDelayMs, capturePreambleMs, triggerInData, captureFormat,
data);
}
}
/**
* Status codes for {@link SoundModelEvent}
*/
@@ -1118,7 +1196,7 @@ public class SoundTrigger {
public static final int SERVICE_STATE_DISABLED = 1;
/**
* Returns a list of descriptors for all harware modules loaded.
* Returns a list of descriptors for all hardware modules loaded.
* @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

View File

@@ -31,6 +31,7 @@ import android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra;
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.media.AudioFormat;
import android.os.AsyncTask;
import android.os.Handler;
@@ -616,7 +617,11 @@ public class AlwaysOnHotwordDetector {
}
@Override
public void onDetected(KeyphraseRecognitionEvent event) {
public void onDetected(RecognitionEvent event) {
if (! (event instanceof KeyphraseRecognitionEvent)) {
Slog.e(TAG, "onDetected() called for a soundtrigger event.");
return;
}
if (DBG) {
Slog.d(TAG, "onDetected(" + event + ")");
} else {

View File

@@ -0,0 +1,42 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.internal.app;
import android.hardware.soundtrigger.IRecognitionStatusCallback;
import android.hardware.soundtrigger.SoundTrigger;
import android.os.ParcelUuid;
/**
* Service interface for a generic sound recognition model.
* @hide
*/
interface ISoundTriggerService {
SoundTrigger.GenericSoundModel getSoundModel(in ParcelUuid soundModelId);
void updateSoundModel(in SoundTrigger.GenericSoundModel soundModel);
void deleteSoundModel(in ParcelUuid soundModelId);
void startRecognition(in ParcelUuid soundModelId, in IRecognitionStatusCallback callback);
/**
* Stops recognition.
*/
void stopRecognition(in ParcelUuid soundModelId, in IRecognitionStatusCallback callback);
}

View File

@@ -2860,6 +2860,12 @@
<permission android:name="android.permission.RECEIVE_MEDIA_RESOURCE_USAGE"
android:protectionLevel="signature|privileged" />
<!-- Must be required by system/priv apps when accessing the sound trigger
APIs given by {@link SoundTriggerManager}.
@hide <p>Not for use by third-party applications.</p> -->
<permission android:name="android.permission.MANAGE_SOUND_TRIGGER"
android:protectionLevel="signature|privileged" />
<application android:process="system"
android:persistent="true"
android:hasCode="false"

View File

@@ -0,0 +1,180 @@
/**
* 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.media.soundtrigger;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.hardware.soundtrigger.IRecognitionStatusCallback;
import android.hardware.soundtrigger.SoundTrigger;
import android.os.Handler;
import android.os.ParcelUuid;
import android.os.RemoteException;
import android.util.Slog;
import com.android.internal.app.ISoundTriggerService;
import java.io.PrintWriter;
import java.util.UUID;
/**
* A class that allows interaction with the actual sound trigger detection on the system.
* Sound trigger detection refers to a detectors that match generic sound patterns that are
* not voice-based. The voice-based recognition models should utilize the {@link
* VoiceInteractionService} instead. Access to this class is protected by a permission
* granted only to system or privileged apps.
*
* @hide
*/
public final class SoundTriggerDetector {
private static final boolean DBG = false;
private static final String TAG = "SoundTriggerDetector";
private final Object mLock = new Object();
private final ISoundTriggerService mSoundTriggerService;
private final UUID mSoundModelId;
private final Callback mCallback;
private final Handler mHandler;
private final RecognitionCallback mRecognitionCallback;
public abstract class Callback {
/**
* Called when the availability of the sound model changes.
*/
public abstract void onAvailabilityChanged(int status);
/**
* Called when the sound model has triggered (such as when it matched a
* given sound pattern).
*/
public abstract void onDetected();
/**
* Called when the detection fails due to an error.
*/
public abstract void onError();
/**
* Called when the recognition is paused temporarily for some reason.
* This is an informational callback, and the clients shouldn't be doing anything here
* except showing an indication on their UI if they have to.
*/
public abstract void onRecognitionPaused();
/**
* Called when the recognition is resumed after it was temporarily paused.
* This is an informational callback, and the clients shouldn't be doing anything here
* except showing an indication on their UI if they have to.
*/
public abstract void onRecognitionResumed();
}
/**
* This class should be constructed by the {@link SoundTriggerManager}.
* @hide
*/
SoundTriggerDetector(ISoundTriggerService soundTriggerService, UUID soundModelId,
@NonNull Callback callback, @Nullable Handler handler) {
mSoundTriggerService = soundTriggerService;
mSoundModelId = soundModelId;
mCallback = callback;
if (handler == null) {
mHandler = new Handler();
} else {
mHandler = handler;
}
mRecognitionCallback = new RecognitionCallback();
}
/**
* Starts recognition on the associated sound model. Result is indicated via the
* {@link Callback}.
* @return Indicates whether the call succeeded or not.
*/
public boolean startRecognition() {
if (DBG) {
Slog.d(TAG, "startRecognition()");
}
try {
mSoundTriggerService.startRecognition(new ParcelUuid(mSoundModelId),
mRecognitionCallback);
} catch (RemoteException e) {
return false;
}
return true;
}
/**
* Stops recognition for the associated model.
*/
public boolean stopRecognition() {
try {
mSoundTriggerService.stopRecognition(new ParcelUuid(mSoundModelId),
mRecognitionCallback);
} catch (RemoteException e) {
return false;
}
return true;
}
public void dump(String prefix, PrintWriter pw) {
synchronized (mLock) {
// TODO: Dump useful debug information.
}
}
/**
* Callback that handles events from the lower sound trigger layer.
* @hide
*/
private static class RecognitionCallback extends
IRecognitionStatusCallback.Stub {
/**
* @hide
*/
@Override
public void onDetected(SoundTrigger.RecognitionEvent event) {
Slog.e(TAG, "onDetected()" + event);
}
/**
* @hide
*/
@Override
public void onError(int status) {
Slog.e(TAG, "onError()" + status);
}
/**
* @hide
*/
@Override
public void onRecognitionPaused() {
Slog.e(TAG, "onRecognitionPaused()");
}
/**
* @hide
*/
@Override
public void onRecognitionResumed() {
Slog.e(TAG, "onRecognitionResumed()");
}
}
}

View File

@@ -0,0 +1,172 @@
/**
* 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.media.soundtrigger;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.content.Context;
import android.hardware.soundtrigger.SoundTrigger;
import android.os.Handler;
import android.os.ParcelUuid;
import android.os.RemoteException;
import android.util.Slog;
import com.android.internal.app.ISoundTriggerService;
import java.util.HashMap;
import java.util.UUID;
/**
* This class provides management of non-voice (general sound trigger) based sound recognition
* models. Usage of this class is restricted to system or signature applications only. This allows
* OEMs to write apps that can manage non-voice based sound trigger models.
*
* @hide
* TODO: Mark this as a SystemApi and get approval.
*/
public final class SoundTriggerManager {
private static final boolean DBG = false;
private static final String TAG = "SoundTriggerManager";
private final Context mContext;
private final ISoundTriggerService mSoundTriggerService;
// Stores a mapping from the sound model UUID to the SoundTriggerInstance created by
// the createSoundTriggerDetector() call.
private final HashMap<UUID, SoundTriggerDetector> mReceiverInstanceMap;
/**
* @hide
*/
public SoundTriggerManager(Context context, ISoundTriggerService soundTriggerService ) {
if (DBG) {
Slog.i(TAG, "SoundTriggerManager created.");
}
mSoundTriggerService = soundTriggerService;
mContext = context;
mReceiverInstanceMap = new HashMap<UUID, SoundTriggerDetector>();
}
/**
* Updates the given sound trigger model.
*/
public void updateModel(Model model) {
try {
mSoundTriggerService.updateSoundModel(model.getGenericSoundModel());
} catch (RemoteException e) {
}
}
/**
* Returns the sound trigger model represented by the given UUID. An instance of {@link Model}
* is returned.
*/
public Model getModel(UUID soundModelId) {
try {
return new Model(mSoundTriggerService.getSoundModel(
new ParcelUuid(soundModelId)));
} catch (RemoteException e) {
return null;
}
}
/**
* Deletes the sound model represented by the provided UUID.
*/
public void deleteModel(UUID soundModelId) {
try {
mSoundTriggerService.deleteSoundModel(new ParcelUuid(soundModelId));
} catch (RemoteException e) {
}
}
/**
* Creates an instance of {@link SoundTriggerDetector} which can be used to start/stop
* recognition on the model and register for triggers from the model. Note that this call
* invalidates any previously returned instances for the same sound model Uuid.
*
* @param soundModelId UUID of the sound model to create the receiver object for.
* @param callback Instance of the {@link SoundTriggerDetector#Callback} object for the
* callbacks for the given sound model.
* @param handler The Handler to use for the callback operations. A null value will use the
* current thread's Looper.
* @return Instance of {@link SoundTriggerDetector} or null on error.
*/
@Nullable
public SoundTriggerDetector createSoundTriggerDetector(UUID soundModelId,
@NonNull SoundTriggerDetector.Callback callback, @Nullable Handler handler) {
if (soundModelId == null) {
return null;
}
SoundTriggerDetector oldInstance = mReceiverInstanceMap.get(soundModelId);
if (oldInstance != null) {
// Shutdown old instance.
}
SoundTriggerDetector newInstance = new SoundTriggerDetector(mSoundTriggerService,
soundModelId, callback, handler);
mReceiverInstanceMap.put(soundModelId, newInstance);
return newInstance;
}
/**
* Class captures the data and fields that represent a non-keyphrase sound model. Use the
* factory constructor {@link Model#create()} to create an instance.
*/
// We use encapsulation to expose the SoundTrigger.GenericSoundModel as a SystemApi. This
// prevents us from exposing SoundTrigger.GenericSoundModel as an Api.
public static class Model {
private SoundTrigger.GenericSoundModel mGenericSoundModel;
/**
* @hide
*/
Model(SoundTrigger.GenericSoundModel soundTriggerModel) {
mGenericSoundModel = soundTriggerModel;
}
/**
* Factory constructor to create a SoundModel instance for use with methods in this
* class.
*/
public static Model create(UUID modelUuid, UUID vendorUuid, byte[] data) {
return new Model(new SoundTrigger.GenericSoundModel(modelUuid,
vendorUuid, data));
}
public UUID getModelUuid() {
return mGenericSoundModel.uuid;
}
public UUID getVendorUuid() {
return mGenericSoundModel.vendorUuid;
}
public byte[] getModelData() {
return mGenericSoundModel.data;
}
/**
* @hide
*/
SoundTrigger.GenericSoundModel getGenericSoundModel() {
return mGenericSoundModel;
}
}
}

View File

@@ -84,6 +84,7 @@ import com.android.server.pm.UserManagerService;
import com.android.server.power.PowerManagerService;
import com.android.server.power.ShutdownThread;
import com.android.server.restrictions.RestrictionsManagerService;
import com.android.server.soundtrigger.SoundTriggerService;
import com.android.server.statusbar.StatusBarManagerService;
import com.android.server.storage.DeviceStorageMonitorService;
import com.android.server.telecom.TelecomLoaderService;
@@ -959,6 +960,8 @@ public final class SystemServer {
mSystemServiceManager.startService(JobSchedulerService.class);
mSystemServiceManager.startService(SoundTriggerService.class);
if (!disableNonCoreServices) {
if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_BACKUP)) {
mSystemServiceManager.startService(BACKUP_MANAGER_SERVICE_CLASS);

View File

@@ -0,0 +1,144 @@
/**
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.soundtrigger;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.hardware.soundtrigger.SoundTrigger;
import android.hardware.soundtrigger.SoundTrigger.GenericSoundModel;
import android.text.TextUtils;
import android.util.Slog;
import java.util.Locale;
import java.util.UUID;
/**
* Helper to manage the database of the sound models that have been registered on the device.
*
* @hide
*/
public class SoundTriggerDbHelper extends SQLiteOpenHelper {
static final String TAG = "SoundTriggerDbHelper";
static final boolean DBG = false;
private static final String NAME = "st_sound_model.db";
private static final int VERSION = 1;
// Sound trigger-based sound models.
public static interface GenericSoundModelContract {
public static final String TABLE = "st_sound_model";
public static final String KEY_MODEL_UUID = "model_uuid";
public static final String KEY_VENDOR_UUID = "vendor_uuid";
public static final String KEY_DATA = "data";
}
// Table Create Statement for the sound trigger table
private static final String CREATE_TABLE_ST_SOUND_MODEL = "CREATE TABLE "
+ GenericSoundModelContract.TABLE + "("
+ GenericSoundModelContract.KEY_MODEL_UUID + " TEXT PRIMARY KEY,"
+ GenericSoundModelContract.KEY_DATA + " BLOB" + " )";
public SoundTriggerDbHelper(Context context) {
super(context, NAME, null, VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
// creating required tables
db.execSQL(CREATE_TABLE_ST_SOUND_MODEL);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// TODO: For now, drop older tables and recreate new ones.
db.execSQL("DROP TABLE IF EXISTS " + GenericSoundModelContract.TABLE);
onCreate(db);
}
/**
* Updates the given sound trigger model, adds it, if it doesn't already exist.
*
*/
public boolean updateGenericSoundModel(GenericSoundModel soundModel) {
synchronized(this) {
SQLiteDatabase db = getWritableDatabase();
ContentValues values = new ContentValues();
values.put(GenericSoundModelContract.KEY_MODEL_UUID, soundModel.uuid.toString());
values.put(GenericSoundModelContract.KEY_VENDOR_UUID, soundModel.vendorUuid.toString());
values.put(GenericSoundModelContract.KEY_DATA, soundModel.data);
try {
return db.insertWithOnConflict(GenericSoundModelContract.TABLE, null, values,
SQLiteDatabase.CONFLICT_REPLACE) != -1;
} finally {
db.close();
}
}
}
public GenericSoundModel getGenericSoundModel(UUID model_uuid) {
synchronized(this) {
// Find the corresponding sound model ID for the keyphrase.
String selectQuery = "SELECT * FROM " + GenericSoundModelContract.TABLE
+ " WHERE " + GenericSoundModelContract.KEY_MODEL_UUID + "= '" +
model_uuid + "'";
SQLiteDatabase db = getReadableDatabase();
Cursor c = db.rawQuery(selectQuery, null);
try {
if (c.moveToFirst()) {
do {
byte[] data = c.getBlob(c.getColumnIndex(
GenericSoundModelContract.KEY_DATA));
String vendor_uuid = c.getString(
c.getColumnIndex(GenericSoundModelContract.KEY_VENDOR_UUID));
return new GenericSoundModel(model_uuid, UUID.fromString(vendor_uuid),
data);
} while (c.moveToNext());
}
} finally {
c.close();
db.close();
}
}
return null;
}
public boolean deleteGenericSoundModel(UUID model_uuid) {
synchronized(this) {
GenericSoundModel soundModel = getGenericSoundModel(model_uuid);
if (soundModel == null) {
return false;
}
// Delete all sound models for the given keyphrase and specified user.
SQLiteDatabase db = getWritableDatabase();
String soundModelClause = GenericSoundModelContract.KEY_MODEL_UUID
+ "='" + soundModel.uuid.toString() + "'";
try {
return db.delete(GenericSoundModelContract.TABLE, soundModelClause, null) != 0;
} finally {
db.close();
}
}
}
}

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.android.server.voiceinteraction;
package com.android.server.soundtrigger;
import android.content.BroadcastReceiver;
import android.content.Context;

View File

@@ -0,0 +1,84 @@
/**
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.soundtrigger;
import android.hardware.soundtrigger.IRecognitionStatusCallback;
import android.hardware.soundtrigger.SoundTrigger;
import android.hardware.soundtrigger.SoundTrigger.Keyphrase;
import android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionEvent;
import android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra;
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.SoundTrigger.SoundModelEvent;
import android.hardware.soundtrigger.SoundTriggerModule;
import java.io.FileDescriptor;
import java.io.PrintWriter;
/**
* Provides a local service for managing voice-related recoginition models. This is primarily used
* by the {@link VoiceInteractionManagerService}.
*/
public abstract class SoundTriggerInternal {
/**
* Return codes for {@link #startRecognition(int, KeyphraseSoundModel,
* IRecognitionStatusCallback, RecognitionConfig)},
* {@link #stopRecognition(int, IRecognitionStatusCallback)}
*/
public static final int STATUS_ERROR = SoundTrigger.STATUS_ERROR;
public static final int STATUS_OK = SoundTrigger.STATUS_OK;
/** The {@link ModuleProperties} for the system, or null if none exists. */
private ModuleProperties moduleProperties;
/**
* Starts recognition for the given keyphraseId.
*
* @param keyphraseId The identifier of the keyphrase for which
* the recognition is to be started.
* @param soundModel The sound model to use for recognition.
* @param listener The listener for the recognition events related to the given keyphrase.
* @return One of {@link #STATUS_ERROR} or {@link #STATUS_OK}.
*/
public abstract int startRecognition(int keyphraseId, KeyphraseSoundModel soundModel,
IRecognitionStatusCallback listener, RecognitionConfig recognitionConfig);
/**
* 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.
* @param listener The listener for the recognition events related to the given keyphrase.
*
* @return One of {@link #STATUS_ERROR} or {@link #STATUS_OK}.
*/
public abstract int stopRecognition(int keyphraseId, IRecognitionStatusCallback listener);
/**
* Stops all recognitions active currently and clears the internal state.
*/
public abstract void stopAllRecognitions();
public ModuleProperties getModuleProperties() {
return moduleProperties;
}
public abstract void dump(FileDescriptor fd, PrintWriter pw, String[] args);
}

View File

@@ -0,0 +1,194 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.soundtrigger;
import android.content.Context;
import android.content.pm.PackageManager;
import android.Manifest;
import android.hardware.soundtrigger.IRecognitionStatusCallback;
import android.hardware.soundtrigger.SoundTrigger;
import android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel;
import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig;
import android.hardware.soundtrigger.SoundTrigger.SoundTriggerModel;
import android.os.Parcel;
import android.os.ParcelUuid;
import android.os.RemoteException;
import android.util.Slog;
import com.android.server.SystemService;
import com.android.internal.app.ISoundTriggerService;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.UUID;
/**
* A single SystemService to manage all sound/voice-based sound models on the DSP.
* This services provides apis to manage sound trigger-based sound models via
* the ISoundTriggerService interface. This class also publishes a local interface encapsulating
* the functionality provided by {@link SoundTriggerHelper} for use by
* {@link VoiceInteractionManagerService}.
*
* @hide
*/
public class SoundTriggerService extends SystemService {
static final String TAG = "SoundTriggerService";
static final boolean DEBUG = false;
final Context mContext;
private final SoundTriggerServiceStub mServiceStub;
private final LocalSoundTriggerService mLocalSoundTriggerService;
private SoundTriggerDbHelper mDbHelper;
public SoundTriggerService(Context context) {
super(context);
mContext = context;
mServiceStub = new SoundTriggerServiceStub();
mLocalSoundTriggerService = new LocalSoundTriggerService(context);
}
@Override
public void onStart() {
publishBinderService(Context.SOUND_TRIGGER_SERVICE, mServiceStub);
publishLocalService(SoundTriggerInternal.class, mLocalSoundTriggerService);
}
@Override
public void onBootPhase(int phase) {
if (PHASE_SYSTEM_SERVICES_READY == phase) {
mLocalSoundTriggerService.initSoundTriggerHelper();
} else if (PHASE_THIRD_PARTY_APPS_CAN_START == phase) {
mDbHelper = new SoundTriggerDbHelper(mContext);
}
}
@Override
public void onStartUser(int userHandle) {
}
@Override
public void onSwitchUser(int userHandle) {
}
class SoundTriggerServiceStub extends ISoundTriggerService.Stub {
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
try {
return super.onTransact(code, data, reply, flags);
} catch (RuntimeException e) {
// The activity manager only throws security exceptions, so let's
// log all others.
if (!(e instanceof SecurityException)) {
Slog.wtf(TAG, "SoundTriggerService Crash", e);
}
throw e;
}
}
@Override
public void startRecognition(ParcelUuid parcelUuid, IRecognitionStatusCallback callback) {
enforceCallingPermission(Manifest.permission.MANAGE_SOUND_TRIGGER);
if (DEBUG) {
Slog.i(TAG, "startRecognition(): Uuid : " + parcelUuid);
}
}
@Override
public void stopRecognition(ParcelUuid parcelUuid, IRecognitionStatusCallback callback) {
enforceCallingPermission(Manifest.permission.MANAGE_SOUND_TRIGGER);
if (DEBUG) {
Slog.i(TAG, "stopRecognition(): Uuid : " + parcelUuid);
}
}
@Override
public SoundTrigger.SoundTriggerModel getSoundModel(ParcelUuid soundModelId) {
enforceCallingPermission(Manifest.permission.MANAGE_SOUND_TRIGGER);
if (DEBUG) {
Slog.i(TAG, "getSoundModel(): id = " + soundModelId);
}
SoundTrigger.SoundTriggerModel model = mDbHelper.getSoundTriggerModel(soundModelId.getUuid());
if (model == null) {
Slog.e(TAG, "Null model in database.");
}
return model;
}
@Override
public void updateSoundModel(SoundTrigger.SoundTriggerModel soundModel) {
enforceCallingPermission(Manifest.permission.MANAGE_SOUND_TRIGGER);
if (DEBUG) {
Slog.i(TAG, "updateSoundModel(): model = " + soundModel);
}
mDbHelper.updateSoundTriggerModel(soundModel);
}
@Override
public void deleteSoundModel(ParcelUuid soundModelId) {
enforceCallingPermission(Manifest.permission.MANAGE_SOUND_TRIGGER);
if (DEBUG) {
Slog.i(TAG, "deleteSoundModel(): id = " + soundModelId);
}
mDbHelper.deleteSoundTriggerModel(soundModelId.getUuid());
}
}
public final class LocalSoundTriggerService extends SoundTriggerInternal {
private final Context mContext;
private SoundTriggerHelper mSoundTriggerHelper;
LocalSoundTriggerService(Context context) {
mContext = context;
}
void initSoundTriggerHelper() {
if (mSoundTriggerHelper == null) {
mSoundTriggerHelper = new SoundTriggerHelper(mContext);
}
}
@Override
public int startRecognition(int keyphraseId, KeyphraseSoundModel soundModel,
IRecognitionStatusCallback listener, RecognitionConfig recognitionConfig) {
return mSoundTriggerHelper.startRecognition(keyphraseId, soundModel, listener,
recognitionConfig);
}
@Override
public int stopRecognition(int keyphraseId, IRecognitionStatusCallback listener) {
return mSoundTriggerHelper.stopRecognition(keyphraseId, listener);
}
@Override
public void stopAllRecognitions() {
mSoundTriggerHelper.stopAllRecognitions();
}
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
mSoundTriggerHelper.dump(fd, pw, args);
}
}
private void enforceCallingPermission(String permission) {
if (mContext.checkCallingOrSelfPermission(permission)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Caller does not hold the permission " + permission);
}
}
}

View File

@@ -62,6 +62,7 @@ import com.android.internal.app.IVoiceInteractor;
import com.android.internal.content.PackageMonitor;
import com.android.internal.os.BackgroundThread;
import com.android.server.LocalServices;
import com.android.server.soundtrigger.SoundTriggerInternal;
import com.android.server.SystemService;
import com.android.server.UiThread;
@@ -79,15 +80,14 @@ public class VoiceInteractionManagerService extends SystemService {
final Context mContext;
final ContentResolver mResolver;
final DatabaseHelper mDbHelper;
final SoundTriggerHelper mSoundTriggerHelper;
final ActivityManagerInternal mAmInternal;
SoundTriggerInternal mSoundTriggerInternal;
public VoiceInteractionManagerService(Context context) {
super(context);
mContext = context;
mResolver = context.getContentResolver();
mDbHelper = new DatabaseHelper(context);
mSoundTriggerHelper = new SoundTriggerHelper(context);
mServiceStub = new VoiceInteractionManagerServiceStub();
mAmInternal = LocalServices.getService(ActivityManagerInternal.class);
@@ -115,7 +115,9 @@ public class VoiceInteractionManagerService extends SystemService {
@Override
public void onBootPhase(int phase) {
if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
if (PHASE_SYSTEM_SERVICES_READY == phase) {
mSoundTriggerInternal = LocalServices.getService(SoundTriggerInternal.class);
} else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
mServiceStub.systemRunning(isSafeMode());
}
}
@@ -380,7 +382,7 @@ public class VoiceInteractionManagerService extends SystemService {
if (force || mImpl == null || mImpl.mUser != mCurUser
|| !mImpl.mComponent.equals(serviceComponent)) {
mSoundTriggerHelper.stopAllRecognitions();
mSoundTriggerInternal.stopAllRecognitions();
if (mImpl != null) {
mImpl.shutdownLocked();
}
@@ -736,9 +738,9 @@ public class VoiceInteractionManagerService extends SystemService {
mImpl.notifySoundModelsChangedLocked();
}
}
return SoundTriggerHelper.STATUS_OK;
return SoundTriggerInternal.STATUS_OK;
} else {
return SoundTriggerHelper.STATUS_ERROR;
return SoundTriggerInternal.STATUS_ERROR;
}
} finally {
Binder.restoreCallingIdentity(caller);
@@ -759,7 +761,7 @@ public class VoiceInteractionManagerService extends SystemService {
boolean deleted = false;
try {
deleted = mDbHelper.deleteKeyphraseSoundModel(keyphraseId, callingUid, bcp47Locale);
return deleted ? SoundTriggerHelper.STATUS_OK : SoundTriggerHelper.STATUS_ERROR;
return deleted ? SoundTriggerInternal.STATUS_OK : SoundTriggerInternal.STATUS_ERROR;
} finally {
if (deleted) {
synchronized (this) {
@@ -812,7 +814,7 @@ public class VoiceInteractionManagerService extends SystemService {
final long caller = Binder.clearCallingIdentity();
try {
return mSoundTriggerHelper.moduleProperties;
return mSoundTriggerInternal.getModuleProperties();
} finally {
Binder.restoreCallingIdentity(caller);
}
@@ -845,9 +847,9 @@ public class VoiceInteractionManagerService extends SystemService {
|| soundModel.uuid == null
|| soundModel.keyphrases == null) {
Slog.w(TAG, "No matching sound model found in startRecognition");
return SoundTriggerHelper.STATUS_ERROR;
return SoundTriggerInternal.STATUS_ERROR;
} else {
return mSoundTriggerHelper.startRecognition(
return mSoundTriggerInternal.startRecognition(
keyphraseId, soundModel, callback, recognitionConfig);
}
} finally {
@@ -869,7 +871,7 @@ public class VoiceInteractionManagerService extends SystemService {
final long caller = Binder.clearCallingIdentity();
try {
return mSoundTriggerHelper.stopRecognition(keyphraseId, callback);
return mSoundTriggerInternal.stopRecognition(keyphraseId, callback);
} finally {
Binder.restoreCallingIdentity(caller);
}
@@ -1011,7 +1013,7 @@ public class VoiceInteractionManagerService extends SystemService {
}
mImpl.dumpLocked(fd, pw, args);
}
mSoundTriggerHelper.dump(fd, pw, args);
mSoundTriggerInternal.dump(fd, pw, args);
}
private void enforceCallingPermission(String permission) {
@@ -1060,7 +1062,7 @@ public class VoiceInteractionManagerService extends SystemService {
// The user is force stopping our current interactor/recognizer.
// Clear the current settings and restore default state.
synchronized (VoiceInteractionManagerService.this) {
mSoundTriggerHelper.stopAllRecognitions();
mSoundTriggerInternal.stopAllRecognitions();
if (mImpl != null) {
mImpl.shutdownLocked();
mImpl = null;

View File

@@ -0,0 +1,12 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_PACKAGE_NAME := SoundTriggerTestApp
LOCAL_MODULE_TAGS := optional
LOCAL_PRIVILEGED_MODULE := true
include $(BUILD_PACKAGE)

View File

@@ -0,0 +1,17 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.test.soundtrigger">
<uses-permission android:name="android.permission.MANAGE_SOUND_TRIGGER" />
<application
android:permission="android.permission.MANAGE_SOUND_TRIGGER">
<activity
android:name="TestSoundTriggerActivity"
android:label="SoundTrigger Test Application"
android:theme="@android:style/Theme.Material.Light.Voice">
<intent-filter>
<action android:name="com.android.intent.action.MANAGE_SOUND_TRIGGER" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
</application>
</manifest>

View File

@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2014 Google Inc.
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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/enroll"
android:onClick="onEnrollButtonClicked"
android:padding="20dp" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/reenroll"
android:onClick="onReEnrollButtonClicked"
android:padding="20dp" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/unenroll"
android:onClick="onUnEnrollButtonClicked"
android:padding="20dp" />
</LinearLayout>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2014 Google Inc.
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.
-->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enroll">Enroll</string>
<string name="reenroll">Re-enroll</string>
<string name="unenroll">Un-enroll</string>
</resources>

View File

@@ -0,0 +1,115 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.test.soundtrigger;
import android.annotation.Nullable;
import android.content.Context;
import android.hardware.soundtrigger.SoundTrigger;
import android.hardware.soundtrigger.SoundTrigger.SoundTriggerModel;
import android.media.soundtrigger.SoundTriggerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ParcelUuid;
import android.util.Log;
import com.android.internal.app.ISoundTriggerService;
import java.util.UUID;
/**
* Utility class for the managing sound trigger sound models.
*/
public class SoundTriggerUtil {
private static final String TAG = "TestSoundTriggerUtil:Hotsound";
private final ISoundTriggerService mSoundTriggerService;
private final SoundTriggerManager mSoundTriggerManager;
private final Context mContext;
public SoundTriggerUtil(Context context) {
mSoundTriggerService = ISoundTriggerService.Stub.asInterface(
ServiceManager.getService(Context.SOUND_TRIGGER_SERVICE));
mSoundTriggerManager = (SoundTriggerManager) context.getSystemService(
Context.SOUND_TRIGGER_SERVICE);
mContext = context;
}
/**
* Adds/Updates a sound model.
* The sound model must contain a valid UUID.
*
* @param soundModel The sound model to add/update.
*/
public boolean addOrUpdateSoundModel(SoundTriggerModel soundModel) {
try {
mSoundTriggerService.updateSoundModel(soundModel);
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in updateSoundModel", e);
}
return true;
}
public void addOrUpdateSoundModel(SoundTriggerManager.Model soundModel) {
mSoundTriggerManager.updateModel(soundModel);
}
/**
* Gets the sound model for the given keyphrase, null if none exists.
* If a sound model for a given keyphrase exists, and it needs to be updated,
* it should be obtained using this method, updated and then passed in to
* {@link #addOrUpdateSoundModel(SoundTriggerModel)} without changing the IDs.
*
* @param modelId The model ID to look-up the sound model for.
* @return The sound model if one was found, null otherwise.
*/
@Nullable
public SoundTriggerModel getSoundModel(UUID modelId) {
SoundTriggerModel model = null;
try {
model = mSoundTriggerService.getSoundModel(new ParcelUuid(modelId));
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in updateKeyphraseSoundModel");
}
if (model == null) {
Log.w(TAG, "No models present for the gien keyphrase ID");
return null;
} else {
return model;
}
}
/**
* Deletes the sound model for the given keyphrase id.
*
* @param modelId The model ID to look-up the sound model for.
* @return {@code true} if the call succeeds, {@code false} otherwise.
*/
@Nullable
public boolean deleteSoundModel(UUID modelId) {
try {
mSoundTriggerService.deleteSoundModel(new ParcelUuid(modelId));
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in updateSoundModel");
}
return true;
}
public void deleteSoundModelUsingManager(UUID modelId) {
mSoundTriggerManager.deleteModel(modelId);
}
}

View File

@@ -0,0 +1,121 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.test.soundtrigger;
import java.util.Random;
import java.util.UUID;
import android.app.Activity;
import android.hardware.soundtrigger.SoundTrigger;
import android.hardware.soundtrigger.SoundTrigger.SoundTriggerModel;
import android.media.soundtrigger.SoundTriggerManager;
import android.os.Bundle;
import android.os.UserManager;
import android.util.Log;
import android.view.View;
import android.widget.Toast;
public class TestSoundTriggerActivity extends Activity {
private static final String TAG = "TestSoundTriggerActivity";
private static final boolean DBG = true;
private SoundTriggerUtil mSoundTriggerUtil;
private Random mRandom;
private UUID mModelUuid = UUID.randomUUID();
private UUID mModelUuid2 = UUID.randomUUID();
private UUID mVendorUuid = UUID.randomUUID();
@Override
protected void onCreate(Bundle savedInstanceState) {
if (DBG) Log.d(TAG, "onCreate");
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mSoundTriggerUtil = new SoundTriggerUtil(this);
mRandom = new Random();
}
/**
* Called when the user clicks the enroll button.
* Performs a fresh enrollment.
*/
public void onEnrollButtonClicked(View v) {
// Generate a fake model to push.
byte[] data = new byte[1024];
mRandom.nextBytes(data);
SoundTriggerModel model = new SoundTriggerModel(mModelUuid, mVendorUuid, data);
boolean status = mSoundTriggerUtil.addOrUpdateSoundModel(model);
if (status) {
Toast.makeText(
this, "Successfully created sound trigger model UUID=" + mModelUuid, Toast.LENGTH_SHORT)
.show();
} else {
Toast.makeText(this, "Failed to enroll!!!" + mModelUuid, Toast.LENGTH_SHORT).show();
}
// Test the SoundManager API.
SoundTriggerManager.Model tmpModel = SoundTriggerManager.Model.create(mModelUuid2,
mVendorUuid, data);
mSoundTriggerUtil.addOrUpdateSoundModel(tmpModel);
}
/**
* Called when the user clicks the un-enroll button.
* Clears the enrollment information for the user.
*/
public void onUnEnrollButtonClicked(View v) {
SoundTriggerModel soundModel = mSoundTriggerUtil.getSoundModel(mModelUuid);
if (soundModel == null) {
Toast.makeText(this, "Sound model not found!!!", Toast.LENGTH_SHORT).show();
return;
}
boolean status = mSoundTriggerUtil.deleteSoundModel(mModelUuid);
if (status) {
Toast.makeText(this, "Successfully deleted model UUID=" + soundModel.uuid,
Toast.LENGTH_SHORT)
.show();
} else {
Toast.makeText(this, "Failed to delete sound model!!!", Toast.LENGTH_SHORT).show();
}
mSoundTriggerUtil.deleteSoundModelUsingManager(mModelUuid2);
}
/**
* Called when the user clicks the re-enroll button.
* Uses the previously enrolled sound model and makes changes to it before pushing it back.
*/
public void onReEnrollButtonClicked(View v) {
SoundTriggerModel soundModel = mSoundTriggerUtil.getSoundModel(mModelUuid);
if (soundModel == null) {
Toast.makeText(this, "Sound model not found!!!", Toast.LENGTH_SHORT).show();
return;
}
// Generate a fake model to push.
byte[] data = new byte[2048];
mRandom.nextBytes(data);
SoundTriggerModel updated = new SoundTriggerModel(soundModel.uuid,
soundModel.vendorUuid, data);
boolean status = mSoundTriggerUtil.addOrUpdateSoundModel(updated);
if (status) {
Toast.makeText(this, "Successfully re-enrolled, model UUID=" + updated.uuid,
Toast.LENGTH_SHORT)
.show();
} else {
Toast.makeText(this, "Failed to re-enroll!!!", Toast.LENGTH_SHORT).show();
}
}
}