Loading core/java/android/service/voice/AlwaysOnHotwordDetector.java +54 −3 Original line number Diff line number Diff line Loading @@ -16,12 +16,14 @@ package android.service.voice; import android.Manifest; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.hardware.soundtrigger.IRecognitionStatusCallback; import android.hardware.soundtrigger.KeyphraseEnrollmentInfo; import android.hardware.soundtrigger.KeyphraseMetadata; Loading Loading @@ -232,8 +234,10 @@ public class AlwaysOnHotwordDetector { private final Callback mExternalCallback; private final Object mLock = new Object(); private final Handler mHandler; private final Context mContext; private int mAvailability = STATE_NOT_READY; private boolean mIsGrantedHotwordPermission; /** * A ModelParamRange is a representation of supported parameter range for a Loading Loading @@ -408,23 +412,37 @@ public class AlwaysOnHotwordDetector { public abstract void onRecognitionResumed(); } private static boolean hasHotwordPermission(Context context) { return context.checkSelfPermission(Manifest.permission.CAPTURE_AUDIO_HOTWORD) == PackageManager.PERMISSION_GRANTED; } private static boolean hasRecordAudioPermission(Context context) { return context.checkSelfPermission(Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED; } /** * @param context The context to check permission * @param text The keyphrase text to get the detector for. * @param locale The java locale for the detector. * @param callback A non-null Callback for receiving the recognition events. * @param keyphraseEnrollmentInfo The Enrollment info of key phrase * @param modelManagementService A service that allows management of sound models. * @hide */ public AlwaysOnHotwordDetector(String text, Locale locale, Callback callback, public AlwaysOnHotwordDetector(Context context, String text, Locale locale, Callback callback, KeyphraseEnrollmentInfo keyphraseEnrollmentInfo, IVoiceInteractionManagerService modelManagementService) { mText = text; mContext = context; mLocale = locale; mKeyphraseEnrollmentInfo = keyphraseEnrollmentInfo; mExternalCallback = callback; mHandler = new MyHandler(); mInternalCallback = new SoundTriggerListener(mHandler); mModelManagementService = modelManagementService; mIsGrantedHotwordPermission = hasHotwordPermission(mContext); new RefreshAvailabiltyTask().execute(); } Loading Loading @@ -477,6 +495,11 @@ public class AlwaysOnHotwordDetector { @AudioCapabilities public int getSupportedAudioCapabilities() { if (DBG) Slog.d(TAG, "getSupportedAudioCapabilities()"); if (!mIsGrantedHotwordPermission) { return 0; } synchronized (mLock) { return getSupportedAudioCapabilitiesLocked(); } Loading Loading @@ -515,6 +538,12 @@ public class AlwaysOnHotwordDetector { */ public boolean startRecognition(@RecognitionFlags int recognitionFlags) { if (DBG) Slog.d(TAG, "startRecognition(" + recognitionFlags + ")"); if (!mIsGrantedHotwordPermission || !hasRecordAudioPermission(mContext)) { throw new IllegalStateException("Must have the RECORD_AUDIO and CAPTURE_AUDIO_HOTWORD " + "permissions to access the detector."); } synchronized (mLock) { if (mAvailability == STATE_INVALID) { throw new IllegalStateException("startRecognition called on an invalid detector"); Loading Loading @@ -545,6 +574,12 @@ public class AlwaysOnHotwordDetector { */ public boolean stopRecognition() { if (DBG) Slog.d(TAG, "stopRecognition()"); if (!mIsGrantedHotwordPermission || !hasRecordAudioPermission(mContext)) { throw new IllegalStateException("Must have the RECORD_AUDIO and CAPTURE_AUDIO_HOTWORD " + "permissions to access the detector."); } synchronized (mLock) { if (mAvailability == STATE_INVALID) { throw new IllegalStateException("stopRecognition called on an invalid detector"); Loading Loading @@ -582,6 +617,10 @@ public class AlwaysOnHotwordDetector { Slog.d(TAG, "setParameter(" + modelParam + ", " + value + ")"); } if (!mIsGrantedHotwordPermission || !hasRecordAudioPermission(mContext)) { return SoundTrigger.STATUS_INVALID_OPERATION; } synchronized (mLock) { if (mAvailability == STATE_INVALID) { throw new IllegalStateException("setParameter called on an invalid detector"); Loading Loading @@ -609,6 +648,10 @@ public class AlwaysOnHotwordDetector { Slog.d(TAG, "getParameter(" + modelParam + ")"); } if (!mIsGrantedHotwordPermission || !hasRecordAudioPermission(mContext)) { return MODEL_PARAM_THRESHOLD_FACTOR; } synchronized (mLock) { if (mAvailability == STATE_INVALID) { throw new IllegalStateException("getParameter called on an invalid detector"); Loading @@ -634,6 +677,10 @@ public class AlwaysOnHotwordDetector { Slog.d(TAG, "queryParameter(" + modelParam + ")"); } if (!mIsGrantedHotwordPermission || !hasRecordAudioPermission(mContext)) { return null; } synchronized (mLock) { if (mAvailability == STATE_INVALID) { throw new IllegalStateException("queryParameter called on an invalid detector"); Loading Loading @@ -742,8 +789,8 @@ public class AlwaysOnHotwordDetector { */ void onSoundModelsChanged() { synchronized (mLock) { if (mAvailability == STATE_INVALID || mAvailability == STATE_HARDWARE_UNAVAILABLE) { if (mAvailability == STATE_INVALID || mAvailability == STATE_HARDWARE_UNAVAILABLE || !hasRecordAudioPermission(mContext)) { Slog.w(TAG, "Received onSoundModelsChanged for an unsupported keyphrase/config"); return; } Loading Loading @@ -962,6 +1009,10 @@ public class AlwaysOnHotwordDetector { * @return The initial availability without checking the enrollment status. */ private int internalGetInitialAvailability() { if (!mIsGrantedHotwordPermission) { return STATE_HARDWARE_UNAVAILABLE; } synchronized (mLock) { // This detector has already been invalidated. if (mAvailability == STATE_INVALID) { Loading core/java/android/service/voice/VoiceInteractionService.java +1 −1 Original line number Diff line number Diff line Loading @@ -318,7 +318,7 @@ public class VoiceInteractionService extends Service { synchronized (mLock) { // Allow only one concurrent recognition via the APIs. safelyShutdownHotwordDetector(); mHotwordDetector = new AlwaysOnHotwordDetector(keyphrase, locale, callback, mHotwordDetector = new AlwaysOnHotwordDetector(this, keyphrase, locale, callback, mKeyphraseEnrollmentInfo, mSystemService); } return mHotwordDetector; Loading services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java +23 −0 Original line number Diff line number Diff line Loading @@ -1094,6 +1094,9 @@ public class VoiceInteractionManagerService extends SystemService { @Override public ModuleProperties getDspModuleProperties() { // Allow the call if it is granted CAPTURE_AUDIO_HOTWORD. enforceCallingPermission(Manifest.permission.CAPTURE_AUDIO_HOTWORD); // Allow the call if this is the current voice interaction service. synchronized (this) { enforceIsCurrentVoiceInteractionService(); Loading @@ -1110,6 +1113,9 @@ public class VoiceInteractionManagerService extends SystemService { @Override public int startRecognition(int keyphraseId, String bcp47Locale, IRecognitionStatusCallback callback, RecognitionConfig recognitionConfig) { // Allow the call if it is granted RECORD_AUDIO and CAPTURE_AUDIO_HOTWORD. enforceAlwaysOnHotwordPermissions(); // Allow the call if this is the current voice interaction service. synchronized (this) { enforceIsCurrentVoiceInteractionService(); Loading Loading @@ -1145,6 +1151,9 @@ public class VoiceInteractionManagerService extends SystemService { @Override public int stopRecognition(int keyphraseId, IRecognitionStatusCallback callback) { // Allow the call if it is granted RECORD_AUDIO and CAPTURE_AUDIO_HOTWORD. enforceAlwaysOnHotwordPermissions(); // Allow the call if this is the current voice interaction service. synchronized (this) { enforceIsCurrentVoiceInteractionService(); Loading @@ -1160,6 +1169,9 @@ public class VoiceInteractionManagerService extends SystemService { @Override public int setParameter(int keyphraseId, @ModelParams int modelParam, int value) { // Allow the call if it is granted RECORD_AUDIO and CAPTURE_AUDIO_HOTWORD. enforceAlwaysOnHotwordPermissions(); // Allow the call if this is the current voice interaction service. synchronized (this) { enforceIsCurrentVoiceInteractionService(); Loading @@ -1175,6 +1187,9 @@ public class VoiceInteractionManagerService extends SystemService { @Override public int getParameter(int keyphraseId, @ModelParams int modelParam) { // Allow the call if it is granted RECORD_AUDIO and CAPTURE_AUDIO_HOTWORD. enforceAlwaysOnHotwordPermissions(); // Allow the call if this is the current voice interaction service. synchronized (this) { enforceIsCurrentVoiceInteractionService(); Loading @@ -1191,6 +1206,9 @@ public class VoiceInteractionManagerService extends SystemService { @Override @Nullable public ModelParamRange queryParameter(int keyphraseId, @ModelParams int modelParam) { // Allow the call if it is granted RECORD_AUDIO and CAPTURE_AUDIO_HOTWORD. enforceAlwaysOnHotwordPermissions(); // Allow the call if this is the current voice interaction service. synchronized (this) { enforceIsCurrentVoiceInteractionService(); Loading Loading @@ -1454,6 +1472,11 @@ public class VoiceInteractionManagerService extends SystemService { == PackageManager.PERMISSION_GRANTED; } private void enforceAlwaysOnHotwordPermissions() { enforceCallingPermission(Manifest.permission.RECORD_AUDIO); enforceCallingPermission(Manifest.permission.CAPTURE_AUDIO_HOTWORD); } private void enforceCallingPermission(String permission) { if (!isCallerHoldingPermission(permission)) { throw new SecurityException("Caller does not hold the permission " + permission); Loading Loading
core/java/android/service/voice/AlwaysOnHotwordDetector.java +54 −3 Original line number Diff line number Diff line Loading @@ -16,12 +16,14 @@ package android.service.voice; import android.Manifest; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.hardware.soundtrigger.IRecognitionStatusCallback; import android.hardware.soundtrigger.KeyphraseEnrollmentInfo; import android.hardware.soundtrigger.KeyphraseMetadata; Loading Loading @@ -232,8 +234,10 @@ public class AlwaysOnHotwordDetector { private final Callback mExternalCallback; private final Object mLock = new Object(); private final Handler mHandler; private final Context mContext; private int mAvailability = STATE_NOT_READY; private boolean mIsGrantedHotwordPermission; /** * A ModelParamRange is a representation of supported parameter range for a Loading Loading @@ -408,23 +412,37 @@ public class AlwaysOnHotwordDetector { public abstract void onRecognitionResumed(); } private static boolean hasHotwordPermission(Context context) { return context.checkSelfPermission(Manifest.permission.CAPTURE_AUDIO_HOTWORD) == PackageManager.PERMISSION_GRANTED; } private static boolean hasRecordAudioPermission(Context context) { return context.checkSelfPermission(Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED; } /** * @param context The context to check permission * @param text The keyphrase text to get the detector for. * @param locale The java locale for the detector. * @param callback A non-null Callback for receiving the recognition events. * @param keyphraseEnrollmentInfo The Enrollment info of key phrase * @param modelManagementService A service that allows management of sound models. * @hide */ public AlwaysOnHotwordDetector(String text, Locale locale, Callback callback, public AlwaysOnHotwordDetector(Context context, String text, Locale locale, Callback callback, KeyphraseEnrollmentInfo keyphraseEnrollmentInfo, IVoiceInteractionManagerService modelManagementService) { mText = text; mContext = context; mLocale = locale; mKeyphraseEnrollmentInfo = keyphraseEnrollmentInfo; mExternalCallback = callback; mHandler = new MyHandler(); mInternalCallback = new SoundTriggerListener(mHandler); mModelManagementService = modelManagementService; mIsGrantedHotwordPermission = hasHotwordPermission(mContext); new RefreshAvailabiltyTask().execute(); } Loading Loading @@ -477,6 +495,11 @@ public class AlwaysOnHotwordDetector { @AudioCapabilities public int getSupportedAudioCapabilities() { if (DBG) Slog.d(TAG, "getSupportedAudioCapabilities()"); if (!mIsGrantedHotwordPermission) { return 0; } synchronized (mLock) { return getSupportedAudioCapabilitiesLocked(); } Loading Loading @@ -515,6 +538,12 @@ public class AlwaysOnHotwordDetector { */ public boolean startRecognition(@RecognitionFlags int recognitionFlags) { if (DBG) Slog.d(TAG, "startRecognition(" + recognitionFlags + ")"); if (!mIsGrantedHotwordPermission || !hasRecordAudioPermission(mContext)) { throw new IllegalStateException("Must have the RECORD_AUDIO and CAPTURE_AUDIO_HOTWORD " + "permissions to access the detector."); } synchronized (mLock) { if (mAvailability == STATE_INVALID) { throw new IllegalStateException("startRecognition called on an invalid detector"); Loading Loading @@ -545,6 +574,12 @@ public class AlwaysOnHotwordDetector { */ public boolean stopRecognition() { if (DBG) Slog.d(TAG, "stopRecognition()"); if (!mIsGrantedHotwordPermission || !hasRecordAudioPermission(mContext)) { throw new IllegalStateException("Must have the RECORD_AUDIO and CAPTURE_AUDIO_HOTWORD " + "permissions to access the detector."); } synchronized (mLock) { if (mAvailability == STATE_INVALID) { throw new IllegalStateException("stopRecognition called on an invalid detector"); Loading Loading @@ -582,6 +617,10 @@ public class AlwaysOnHotwordDetector { Slog.d(TAG, "setParameter(" + modelParam + ", " + value + ")"); } if (!mIsGrantedHotwordPermission || !hasRecordAudioPermission(mContext)) { return SoundTrigger.STATUS_INVALID_OPERATION; } synchronized (mLock) { if (mAvailability == STATE_INVALID) { throw new IllegalStateException("setParameter called on an invalid detector"); Loading Loading @@ -609,6 +648,10 @@ public class AlwaysOnHotwordDetector { Slog.d(TAG, "getParameter(" + modelParam + ")"); } if (!mIsGrantedHotwordPermission || !hasRecordAudioPermission(mContext)) { return MODEL_PARAM_THRESHOLD_FACTOR; } synchronized (mLock) { if (mAvailability == STATE_INVALID) { throw new IllegalStateException("getParameter called on an invalid detector"); Loading @@ -634,6 +677,10 @@ public class AlwaysOnHotwordDetector { Slog.d(TAG, "queryParameter(" + modelParam + ")"); } if (!mIsGrantedHotwordPermission || !hasRecordAudioPermission(mContext)) { return null; } synchronized (mLock) { if (mAvailability == STATE_INVALID) { throw new IllegalStateException("queryParameter called on an invalid detector"); Loading Loading @@ -742,8 +789,8 @@ public class AlwaysOnHotwordDetector { */ void onSoundModelsChanged() { synchronized (mLock) { if (mAvailability == STATE_INVALID || mAvailability == STATE_HARDWARE_UNAVAILABLE) { if (mAvailability == STATE_INVALID || mAvailability == STATE_HARDWARE_UNAVAILABLE || !hasRecordAudioPermission(mContext)) { Slog.w(TAG, "Received onSoundModelsChanged for an unsupported keyphrase/config"); return; } Loading Loading @@ -962,6 +1009,10 @@ public class AlwaysOnHotwordDetector { * @return The initial availability without checking the enrollment status. */ private int internalGetInitialAvailability() { if (!mIsGrantedHotwordPermission) { return STATE_HARDWARE_UNAVAILABLE; } synchronized (mLock) { // This detector has already been invalidated. if (mAvailability == STATE_INVALID) { Loading
core/java/android/service/voice/VoiceInteractionService.java +1 −1 Original line number Diff line number Diff line Loading @@ -318,7 +318,7 @@ public class VoiceInteractionService extends Service { synchronized (mLock) { // Allow only one concurrent recognition via the APIs. safelyShutdownHotwordDetector(); mHotwordDetector = new AlwaysOnHotwordDetector(keyphrase, locale, callback, mHotwordDetector = new AlwaysOnHotwordDetector(this, keyphrase, locale, callback, mKeyphraseEnrollmentInfo, mSystemService); } return mHotwordDetector; Loading
services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java +23 −0 Original line number Diff line number Diff line Loading @@ -1094,6 +1094,9 @@ public class VoiceInteractionManagerService extends SystemService { @Override public ModuleProperties getDspModuleProperties() { // Allow the call if it is granted CAPTURE_AUDIO_HOTWORD. enforceCallingPermission(Manifest.permission.CAPTURE_AUDIO_HOTWORD); // Allow the call if this is the current voice interaction service. synchronized (this) { enforceIsCurrentVoiceInteractionService(); Loading @@ -1110,6 +1113,9 @@ public class VoiceInteractionManagerService extends SystemService { @Override public int startRecognition(int keyphraseId, String bcp47Locale, IRecognitionStatusCallback callback, RecognitionConfig recognitionConfig) { // Allow the call if it is granted RECORD_AUDIO and CAPTURE_AUDIO_HOTWORD. enforceAlwaysOnHotwordPermissions(); // Allow the call if this is the current voice interaction service. synchronized (this) { enforceIsCurrentVoiceInteractionService(); Loading Loading @@ -1145,6 +1151,9 @@ public class VoiceInteractionManagerService extends SystemService { @Override public int stopRecognition(int keyphraseId, IRecognitionStatusCallback callback) { // Allow the call if it is granted RECORD_AUDIO and CAPTURE_AUDIO_HOTWORD. enforceAlwaysOnHotwordPermissions(); // Allow the call if this is the current voice interaction service. synchronized (this) { enforceIsCurrentVoiceInteractionService(); Loading @@ -1160,6 +1169,9 @@ public class VoiceInteractionManagerService extends SystemService { @Override public int setParameter(int keyphraseId, @ModelParams int modelParam, int value) { // Allow the call if it is granted RECORD_AUDIO and CAPTURE_AUDIO_HOTWORD. enforceAlwaysOnHotwordPermissions(); // Allow the call if this is the current voice interaction service. synchronized (this) { enforceIsCurrentVoiceInteractionService(); Loading @@ -1175,6 +1187,9 @@ public class VoiceInteractionManagerService extends SystemService { @Override public int getParameter(int keyphraseId, @ModelParams int modelParam) { // Allow the call if it is granted RECORD_AUDIO and CAPTURE_AUDIO_HOTWORD. enforceAlwaysOnHotwordPermissions(); // Allow the call if this is the current voice interaction service. synchronized (this) { enforceIsCurrentVoiceInteractionService(); Loading @@ -1191,6 +1206,9 @@ public class VoiceInteractionManagerService extends SystemService { @Override @Nullable public ModelParamRange queryParameter(int keyphraseId, @ModelParams int modelParam) { // Allow the call if it is granted RECORD_AUDIO and CAPTURE_AUDIO_HOTWORD. enforceAlwaysOnHotwordPermissions(); // Allow the call if this is the current voice interaction service. synchronized (this) { enforceIsCurrentVoiceInteractionService(); Loading Loading @@ -1454,6 +1472,11 @@ public class VoiceInteractionManagerService extends SystemService { == PackageManager.PERMISSION_GRANTED; } private void enforceAlwaysOnHotwordPermissions() { enforceCallingPermission(Manifest.permission.RECORD_AUDIO); enforceCallingPermission(Manifest.permission.CAPTURE_AUDIO_HOTWORD); } private void enforceCallingPermission(String permission) { if (!isCallerHoldingPermission(permission)) { throw new SecurityException("Caller does not hold the permission " + permission); Loading