diff options
author | Nicholas Ambur <nambur@google.com> | 2020-02-19 18:11:30 -0800 |
---|---|---|
committer | Nicholas Ambur <nambur@google.com> | 2020-02-25 01:20:05 +0000 |
commit | 1ec50c8f2f547df3677f3fab2ee1c39ee4209063 (patch) | |
tree | a839de82aeb8cbbca7a1ea6de18534c0da77c3e9 | |
parent | 56d9d677e8a0f1dedce778f1ec28a590bcc56fdb (diff) |
remove client token passing active VI service
Previous implementation relied on client to pass a token which the
service used to verify if it was the active service. This is seen to be
a security concern as there is no way to verify how the client obtained
the token. Instead, a check is done to confirm the caller's UID matches
the UID of the active service.
In the case of voice model enrollment, KeyphraseEnrollmentInfo class is
leveraged. A client is allowed to enroll if it is the active voice
interaction service or if it is a voice model enrollment application
bundled with the system image.
All previous manifest permision checks still apply.
Bug: 148159858
Test: gts-tradefed run gts-dev -m GtsAssistIntentTestCases -t \
com.google.android.assist.gts.KeyphraseModelManagerTest \
\#testShouldEnrollOnlyWhenActiveService
Merged-In: Ie2c4653d365770a9123a22bc69822518b4ccc568
Change-Id: Ie2c4653d365770a9123a22bc69822518b4ccc568
(cherry picked from commit c6f4118f9e86f666817bc10a5dbae51d0dabacb8)
6 files changed, 147 insertions, 81 deletions
diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java index a88d389178bf..7d070b1d9df6 100644 --- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java +++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java @@ -227,7 +227,6 @@ public class AlwaysOnHotwordDetector { @Nullable private KeyphraseMetadata mKeyphraseMetadata; private final KeyphraseEnrollmentInfo mKeyphraseEnrollmentInfo; - private final IVoiceInteractionService mVoiceInteractionService; private final IVoiceInteractionManagerService mModelManagementService; private final SoundTriggerListener mInternalCallback; private final Callback mExternalCallback; @@ -413,14 +412,11 @@ public class AlwaysOnHotwordDetector { * @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 voiceInteractionService The current voice interaction service. * @param modelManagementService A service that allows management of sound models. - * * @hide */ public AlwaysOnHotwordDetector(String text, Locale locale, Callback callback, KeyphraseEnrollmentInfo keyphraseEnrollmentInfo, - IVoiceInteractionService voiceInteractionService, IVoiceInteractionManagerService modelManagementService) { mText = text; mLocale = locale; @@ -428,7 +424,6 @@ public class AlwaysOnHotwordDetector { mExternalCallback = callback; mHandler = new MyHandler(); mInternalCallback = new SoundTriggerListener(mHandler); - mVoiceInteractionService = voiceInteractionService; mModelManagementService = modelManagementService; new RefreshAvailabiltyTask().execute(); } @@ -471,6 +466,8 @@ public class AlwaysOnHotwordDetector { /** * Get the audio capabilities supported by the platform which can be enabled when * starting a recognition. + * Caller must be the active voice interaction service via + * Settings.Secure.VOICE_INTERACTION_SERVICE. * * @see #AUDIO_CAPABILITY_ECHO_CANCELLATION * @see #AUDIO_CAPABILITY_NOISE_SUPPRESSION @@ -488,7 +485,7 @@ public class AlwaysOnHotwordDetector { private int getSupportedAudioCapabilitiesLocked() { try { ModuleProperties properties = - mModelManagementService.getDspModuleProperties(mVoiceInteractionService); + mModelManagementService.getDspModuleProperties(); if (properties != null) { return properties.audioCapabilities; } @@ -501,6 +498,8 @@ public class AlwaysOnHotwordDetector { /** * Starts recognition for the associated keyphrase. + * Caller must be the active voice interaction service via + * Settings.Secure.VOICE_INTERACTION_SERVICE. * * @see #RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO * @see #RECOGNITION_FLAG_ALLOW_MULTIPLE_TRIGGERS @@ -533,6 +532,8 @@ public class AlwaysOnHotwordDetector { /** * Stops recognition for the associated keyphrase. + * Caller must be the active voice interaction service via + * Settings.Secure.VOICE_INTERACTION_SERVICE. * * @return Indicates whether the call succeeded or not. * @throws UnsupportedOperationException if the recognition isn't supported. @@ -565,6 +566,8 @@ public class AlwaysOnHotwordDetector { * stopping recognition. Once the model is unloaded, the value will be lost. * {@link AlwaysOnHotwordDetector#queryParameter} should be checked first before calling this * method. + * Caller must be the active voice interaction service via + * Settings.Secure.VOICE_INTERACTION_SERVICE. * * @param modelParam {@link ModelParams} * @param value Value to set @@ -595,6 +598,8 @@ public class AlwaysOnHotwordDetector { * value is returned. See {@link ModelParams} for parameter default values. * {@link AlwaysOnHotwordDetector#queryParameter} should be checked first before * calling this method. + * Caller must be the active voice interaction service via + * Settings.Secure.VOICE_INTERACTION_SERVICE. * * @param modelParam {@link ModelParams} * @return value of parameter @@ -617,6 +622,8 @@ public class AlwaysOnHotwordDetector { * Determine if parameter control is supported for the given model handle. * This method should be checked prior to calling {@link AlwaysOnHotwordDetector#setParameter} * or {@link AlwaysOnHotwordDetector#getParameter}. + * Caller must be the active voice interaction service via + * Settings.Secure.VOICE_INTERACTION_SERVICE. * * @param modelParam {@link ModelParams} * @return supported range of parameter, null if not supported @@ -775,7 +782,7 @@ public class AlwaysOnHotwordDetector { int code = STATUS_ERROR; try { - code = mModelManagementService.startRecognition(mVoiceInteractionService, + code = mModelManagementService.startRecognition( mKeyphraseMetadata.id, mLocale.toLanguageTag(), mInternalCallback, new RecognitionConfig(captureTriggerAudio, allowMultipleTriggers, recognitionExtra, null /* additional data */, audioCapabilities)); @@ -791,8 +798,8 @@ public class AlwaysOnHotwordDetector { private int stopRecognitionLocked() { int code = STATUS_ERROR; try { - code = mModelManagementService.stopRecognition( - mVoiceInteractionService, mKeyphraseMetadata.id, mInternalCallback); + code = mModelManagementService.stopRecognition(mKeyphraseMetadata.id, + mInternalCallback); } catch (RemoteException e) { Slog.w(TAG, "RemoteException in stopRecognition!", e); } @@ -805,8 +812,8 @@ public class AlwaysOnHotwordDetector { private int setParameterLocked(@ModelParams int modelParam, int value) { try { - int code = mModelManagementService.setParameter(mVoiceInteractionService, - mKeyphraseMetadata.id, modelParam, value); + int code = mModelManagementService.setParameter(mKeyphraseMetadata.id, modelParam, + value); if (code != STATUS_OK) { Slog.w(TAG, "setParameter failed with error code " + code); @@ -820,8 +827,7 @@ public class AlwaysOnHotwordDetector { private int getParameterLocked(@ModelParams int modelParam) { try { - return mModelManagementService.getParameter(mVoiceInteractionService, - mKeyphraseMetadata.id, modelParam); + return mModelManagementService.getParameter(mKeyphraseMetadata.id, modelParam); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -831,8 +837,7 @@ public class AlwaysOnHotwordDetector { private ModelParamRange queryParameterLocked(@ModelParams int modelParam) { try { SoundTrigger.ModelParamRange modelParamRange = - mModelManagementService.queryParameter(mVoiceInteractionService, - mKeyphraseMetadata.id, modelParam); + mModelManagementService.queryParameter(mKeyphraseMetadata.id, modelParam); if (modelParamRange == null) { return null; @@ -966,7 +971,7 @@ public class AlwaysOnHotwordDetector { ModuleProperties dspModuleProperties = null; try { dspModuleProperties = - mModelManagementService.getDspModuleProperties(mVoiceInteractionService); + mModelManagementService.getDspModuleProperties(); } catch (RemoteException e) { Slog.w(TAG, "RemoteException in getDspProperties!", e); } @@ -982,7 +987,7 @@ public class AlwaysOnHotwordDetector { private void internalUpdateEnrolledKeyphraseMetadata() { try { mKeyphraseMetadata = mModelManagementService.getEnrolledKeyphraseMetadata( - mVoiceInteractionService, mText, mLocale.toLanguageTag()); + mText, mLocale.toLanguageTag()); } catch (RemoteException e) { Slog.w(TAG, "RemoteException in internalUpdateEnrolledKeyphraseMetadata", e); } diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java index fc99836b82fd..b54e4d9b876d 100644 --- a/core/java/android/service/voice/VoiceInteractionService.java +++ b/core/java/android/service/voice/VoiceInteractionService.java @@ -199,7 +199,7 @@ public class VoiceInteractionService extends Service { throw new IllegalStateException("Not available until onReady() is called"); } try { - mSystemService.showSession(mInterface, args, flags); + mSystemService.showSession(args, flags); } catch (RemoteException e) { } } @@ -302,7 +302,7 @@ public class VoiceInteractionService extends Service { // Allow only one concurrent recognition via the APIs. safelyShutdownHotwordDetector(); mHotwordDetector = new AlwaysOnHotwordDetector(keyphrase, locale, callback, - mKeyphraseEnrollmentInfo, mInterface, mSystemService); + mKeyphraseEnrollmentInfo, mSystemService); } return mHotwordDetector; } @@ -373,7 +373,7 @@ public class VoiceInteractionService extends Service { } try { - mSystemService.setUiHints(mInterface, hints); + mSystemService.setUiHints(hints); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl index 417e23f56448..71ee8af8b11a 100644 --- a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl +++ b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl @@ -33,7 +33,7 @@ import android.service.voice.IVoiceInteractionService; import android.service.voice.IVoiceInteractionSession; interface IVoiceInteractionManagerService { - void showSession(IVoiceInteractionService service, in Bundle sessionArgs, int flags); + void showSession(in Bundle sessionArgs, int flags); boolean deliverNewSession(IBinder token, IVoiceInteractionSession session, IVoiceInteractor interactor); boolean showSessionFromSession(IBinder token, in Bundle sessionArgs, int flags); @@ -52,68 +52,91 @@ interface IVoiceInteractionManagerService { /** * Gets the registered Sound model for keyphrase detection for the current user. * May be null if no matching sound model exists. + * Caller must either be the active voice interaction service via + * {@link Settings.Secure.VOICE_INTERACTION_SERVICE}, or the caller must be a voice model + * enrollment application detected by + * {@link android.hardware.soundtrigger.KeyphraseEnrollmentInfo}. * * @param keyphraseId The unique identifier for the keyphrase. * @param bcp47Locale The BCP47 language tag for the keyphrase's locale. + * @RequiresPermission Manifest.permission.MANAGE_VOICE_KEYPHRASES */ @UnsupportedAppUsage SoundTrigger.KeyphraseSoundModel getKeyphraseSoundModel(int keyphraseId, in String bcp47Locale); /** - * Add/Update the given keyphrase sound model. + * Add/Update the given keyphrase sound model for the current user. + * Caller must either be the active voice interaction service via + * {@link Settings.Secure.VOICE_INTERACTION_SERVICE}, or the caller must be a voice model + * enrollment application detected by + * {@link android.hardware.soundtrigger.KeyphraseEnrollmentInfo}. + * + * @param model The keyphrase sound model to store peristantly. + * @RequiresPermission Manifest.permission.MANAGE_VOICE_KEYPHRASES */ int updateKeyphraseSoundModel(in SoundTrigger.KeyphraseSoundModel model); /** * Deletes the given keyphrase sound model for the current user. + * Caller must either be the active voice interaction service via + * {@link Settings.Secure.VOICE_INTERACTION_SERVICE}, or the caller must be a voice model + * enrollment application detected by + * {@link android.hardware.soundtrigger.KeyphraseEnrollmentInfo}. * * @param keyphraseId The unique identifier for the keyphrase. * @param bcp47Locale The BCP47 language tag for the keyphrase's locale. + * @RequiresPermission Manifest.permission.MANAGE_VOICE_KEYPHRASES */ int deleteKeyphraseSoundModel(int keyphraseId, in String bcp47Locale); /** * Gets the properties of the DSP hardware on this device, null if not present. + * Caller must be the active voice interaction service via + * {@link Settings.Secure.VOICE_INTERACTION_SERVICE}. */ - SoundTrigger.ModuleProperties getDspModuleProperties(in IVoiceInteractionService service); + SoundTrigger.ModuleProperties getDspModuleProperties(); /** * Indicates if there's a keyphrase sound model available for the given keyphrase ID and the * user ID of the caller. + * Caller must be the active voice interaction service via + * {@link Settings.Secure.VOICE_INTERACTION_SERVICE}. * - * @param service The current VoiceInteractionService. * @param keyphraseId The unique identifier for the keyphrase. * @param bcp47Locale The BCP47 language tag for the keyphrase's locale. */ - boolean isEnrolledForKeyphrase(IVoiceInteractionService service, int keyphraseId, - String bcp47Locale); + boolean isEnrolledForKeyphrase(int keyphraseId, String bcp47Locale); /** * Generates KeyphraseMetadata for an enrolled sound model based on keyphrase string, locale, * and the user ID of the caller. + * Caller must be the active voice interaction service via + * {@link Settings.Secure.VOICE_INTERACTION_SERVICE}. * - * @param service The current VoiceInteractionService * @param keyphrase Keyphrase text associated with the enrolled model * @param bcp47Locale The BCP47 language tag for the keyphrase's locale. * @return The metadata for the enrolled voice model bassed on the passed in parameters. Null if * no matching voice model exists. */ - KeyphraseMetadata getEnrolledKeyphraseMetadata(IVoiceInteractionService service, - String keyphrase, String bcp47Locale); + KeyphraseMetadata getEnrolledKeyphraseMetadata(String keyphrase, String bcp47Locale); /** * Starts a recognition for the given keyphrase. + * Caller must be the active voice interaction service via + * {@link Settings.Secure.VOICE_INTERACTION_SERVICE}. */ - int startRecognition(in IVoiceInteractionService service, int keyphraseId, - in String bcp47Locale, in IRecognitionStatusCallback callback, + int startRecognition(int keyphraseId, in String bcp47Locale, + in IRecognitionStatusCallback callback, in SoundTrigger.RecognitionConfig recognitionConfig); /** * Stops a recognition for the given keyphrase. + * Caller must be the active voice interaction service via + * {@link Settings.Secure.VOICE_INTERACTION_SERVICE}. */ - int stopRecognition(in IVoiceInteractionService service, int keyphraseId, - in IRecognitionStatusCallback callback); + int stopRecognition(int keyphraseId, in IRecognitionStatusCallback callback); /** * Set a model specific ModelParams with the given value. 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. * queryParameter should be checked first before calling this method. + * Caller must be the active voice interaction service via + * {@link Settings.Secure.VOICE_INTERACTION_SERVICE}. * - * @param service The current VoiceInteractionService. * @param keyphraseId The unique identifier for the keyphrase. * @param modelParam ModelParams * @param value Value to set @@ -123,36 +146,37 @@ interface IVoiceInteractionManagerService { * - {@link SoundTrigger#STATUS_INVALID_OPERATION} if the call is out of sequence or * if API is not supported by HAL */ - int setParameter(in IVoiceInteractionService service, int keyphraseId, - in ModelParams modelParam, int value); + int setParameter(int keyphraseId, in ModelParams modelParam, int value); /** * Get a model specific 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 ModelParams for parameter default values. * queryParameter should be checked first before calling this method. + * Caller must be the active voice interaction service via + * {@link Settings.Secure.VOICE_INTERACTION_SERVICE}. * - * @param service The current VoiceInteractionService. * @param keyphraseId The unique identifier for the keyphrase. * @param modelParam ModelParams * @return value of parameter */ - int getParameter(in IVoiceInteractionService service, int keyphraseId, - in ModelParams modelParam); + int getParameter(int keyphraseId, in ModelParams modelParam); /** * Determine if parameter control is supported for the given model handle. * This method should be checked prior to calling setParameter or getParameter. + * Caller must be the active voice interaction service via + * {@link Settings.Secure.VOICE_INTERACTION_SERVICE}. * - * @param service The current VoiceInteractionService. * @param keyphraseId The unique identifier for the keyphrase. * @param modelParam ModelParams * @return supported range of parameter, null if not supported */ - @nullable SoundTrigger.ModelParamRange queryParameter(in IVoiceInteractionService service, - int keyphraseId, in ModelParams modelParam); + @nullable SoundTrigger.ModelParamRange queryParameter(int keyphraseId, + in ModelParams modelParam); /** * @return the component name for the currently active voice interaction service + * @RequiresPermission Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE */ ComponentName getActiveServiceComponentName(); @@ -164,59 +188,70 @@ interface IVoiceInteractionManagerService { * @param sourceFlags flags indicating the source of this show * @param showCallback optional callback to be notified when the session was shown * @param activityToken optional token of activity that needs to be on top + * @RequiresPermission Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE */ boolean showSessionForActiveService(in Bundle args, int sourceFlags, IVoiceInteractionSessionShowCallback showCallback, IBinder activityToken); /** * Hides the session from the active service, if it is showing. + * @RequiresPermission Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE */ void hideCurrentSession(); /** * Notifies the active service that a launch was requested from the Keyguard. This will only * be called if {@link #activeServiceSupportsLaunchFromKeyguard()} returns true. + * @RequiresPermission Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE */ void launchVoiceAssistFromKeyguard(); /** * Indicates whether there is a voice session running (but not necessarily showing). + * @RequiresPermission Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE */ boolean isSessionRunning(); /** * Indicates whether the currently active voice interaction service is capable of handling the * assist gesture. + * @RequiresPermission Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE */ boolean activeServiceSupportsAssist(); /** * Indicates whether the currently active voice interaction service is capable of being launched * from the lockscreen. + * @RequiresPermission Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE */ boolean activeServiceSupportsLaunchFromKeyguard(); /** * Called when the lockscreen got shown. + * @RequiresPermission Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE */ void onLockscreenShown(); /** * Register a voice interaction listener. + * @RequiresPermission Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE */ void registerVoiceInteractionSessionListener(IVoiceInteractionSessionListener listener); /** * Checks the availability of a set of voice actions for the current active voice service. * Returns all supported voice actions. + * @RequiresPermission Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE */ void getActiveServiceSupportedActions(in List<String> voiceActions, in IVoiceActionCheckCallback callback); /** * Provide hints for showing UI. + * Caller must be the active voice interaction service via + * {@link Settings.Secure.VOICE_INTERACTION_SERVICE}. */ - void setUiHints(in IVoiceInteractionService service, in Bundle hints); + void setUiHints(in Bundle hints); /** * Requests a list of supported actions from a specific activity. diff --git a/media/java/android/media/voice/KeyphraseModelManager.java b/media/java/android/media/voice/KeyphraseModelManager.java index 3fa38e0a5854..8ec8967a353e 100644 --- a/media/java/android/media/voice/KeyphraseModelManager.java +++ b/media/java/android/media/voice/KeyphraseModelManager.java @@ -37,7 +37,8 @@ import java.util.Objects; * manage voice based sound trigger models. * Callers of this class are expected to have whitelist manifest permission MANAGE_VOICE_KEYPHRASES. * Callers of this class are expected to be the designated voice interaction service via - * {@link Settings.Secure.VOICE_INTERACTION_SERVICE}. + * {@link Settings.Secure.VOICE_INTERACTION_SERVICE} or a bundled voice model enrollment application + * detected by {@link android.hardware.soundtrigger.KeyphraseEnrollmentInfo}. * @hide */ @SystemApi @@ -65,6 +66,10 @@ public final class KeyphraseModelManager { * {@link #updateKeyphraseSoundModel}. * If the active voice interaction service changes from the current user, all requests will be * rejected, and any registered models will be unregistered. + * Caller must either be the active voice interaction service via + * {@link Settings.Secure.VOICE_INTERACTION_SERVICE}, or the caller must be a voice model + * enrollment application detected by + * {@link android.hardware.soundtrigger.KeyphraseEnrollmentInfo}. * * @param keyphraseId The unique identifier for the keyphrase. * @param locale The locale language tag supported by the desired model. @@ -93,6 +98,10 @@ public final class KeyphraseModelManager { * will be overwritten with the new model. * If the active voice interaction service changes from the current user, all requests will be * rejected, and any registered models will be unregistered. + * Caller must either be the active voice interaction service via + * {@link Settings.Secure.VOICE_INTERACTION_SERVICE}, or the caller must be a voice model + * enrollment application detected by + * {@link android.hardware.soundtrigger.KeyphraseEnrollmentInfo}. * * @param model Keyphrase sound model to be updated. * @throws ServiceSpecificException Thrown with error code if failed to update the keyphrase @@ -120,6 +129,10 @@ public final class KeyphraseModelManager { * {@link #updateKeyphraseSoundModel}. * If the active voice interaction service changes from the current user, all requests will be * rejected, and any registered models will be unregistered. + * Caller must either be the active voice interaction service via + * {@link Settings.Secure.VOICE_INTERACTION_SERVICE}, or the caller must be a voice model + * enrollment application detected by + * {@link android.hardware.soundtrigger.KeyphraseEnrollmentInfo}. * * @param keyphraseId The unique identifier for the keyphrase. * @param locale The locale language tag supported by the desired model. diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java index 3c0e0af67969..0b24dd2fe15a 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java @@ -60,7 +60,6 @@ import android.os.Trace; import android.os.UserHandle; import android.os.UserManagerInternal; import android.provider.Settings; -import android.service.voice.IVoiceInteractionService; import android.service.voice.IVoiceInteractionSession; import android.service.voice.VoiceInteractionManagerInternal; import android.service.voice.VoiceInteractionService; @@ -684,9 +683,9 @@ public class VoiceInteractionManagerService extends SystemService { } @Override - public void showSession(IVoiceInteractionService service, Bundle args, int flags) { + public void showSession(Bundle args, int flags) { synchronized (this) { - enforceIsCurrentVoiceInteractionService(service); + enforceIsCurrentVoiceInteractionService(); final long caller = Binder.clearCallingIdentity(); try { @@ -928,12 +927,10 @@ public class VoiceInteractionManagerService extends SystemService { } //----------------- Model management APIs --------------------------------// - // TODO: add check to only allow active voice interaction service or keyphrase enrollment - // application to manage voice models @Override public KeyphraseSoundModel getKeyphraseSoundModel(int keyphraseId, String bcp47Locale) { - enforceCallingPermission(Manifest.permission.MANAGE_VOICE_KEYPHRASES); + enforceCallerAllowedToEnrollVoiceModel(); if (bcp47Locale == null) { throw new IllegalArgumentException("Illegal argument(s) in getKeyphraseSoundModel"); @@ -950,7 +947,7 @@ public class VoiceInteractionManagerService extends SystemService { @Override public int updateKeyphraseSoundModel(KeyphraseSoundModel model) { - enforceCallingPermission(Manifest.permission.MANAGE_VOICE_KEYPHRASES); + enforceCallerAllowedToEnrollVoiceModel(); if (model == null) { throw new IllegalArgumentException("Model must not be null"); } @@ -975,7 +972,7 @@ public class VoiceInteractionManagerService extends SystemService { @Override public int deleteKeyphraseSoundModel(int keyphraseId, String bcp47Locale) { - enforceCallingPermission(Manifest.permission.MANAGE_VOICE_KEYPHRASES); + enforceCallerAllowedToEnrollVoiceModel(); if (bcp47Locale == null) { throw new IllegalArgumentException( @@ -1008,10 +1005,9 @@ public class VoiceInteractionManagerService extends SystemService { //----------------- SoundTrigger APIs --------------------------------// @Override - public boolean isEnrolledForKeyphrase(IVoiceInteractionService service, int keyphraseId, - String bcp47Locale) { + public boolean isEnrolledForKeyphrase(int keyphraseId, String bcp47Locale) { synchronized (this) { - enforceIsCurrentVoiceInteractionService(service); + enforceIsCurrentVoiceInteractionService(); } if (bcp47Locale == null) { @@ -1030,10 +1026,10 @@ public class VoiceInteractionManagerService extends SystemService { } @Nullable - public KeyphraseMetadata getEnrolledKeyphraseMetadata(IVoiceInteractionService service, - String keyphrase, String bcp47Locale) { + public KeyphraseMetadata getEnrolledKeyphraseMetadata(String keyphrase, + String bcp47Locale) { synchronized (this) { - enforceIsCurrentVoiceInteractionService(service); + enforceIsCurrentVoiceInteractionService(); } if (bcp47Locale == null) { @@ -1065,10 +1061,10 @@ public class VoiceInteractionManagerService extends SystemService { } @Override - public ModuleProperties getDspModuleProperties(IVoiceInteractionService service) { + public ModuleProperties getDspModuleProperties() { // Allow the call if this is the current voice interaction service. synchronized (this) { - enforceIsCurrentVoiceInteractionService(service); + enforceIsCurrentVoiceInteractionService(); final long caller = Binder.clearCallingIdentity(); try { @@ -1080,12 +1076,11 @@ public class VoiceInteractionManagerService extends SystemService { } @Override - public int startRecognition(IVoiceInteractionService service, int keyphraseId, - String bcp47Locale, IRecognitionStatusCallback callback, - RecognitionConfig recognitionConfig) { + public int startRecognition(int keyphraseId, String bcp47Locale, + IRecognitionStatusCallback callback, RecognitionConfig recognitionConfig) { // Allow the call if this is the current voice interaction service. synchronized (this) { - enforceIsCurrentVoiceInteractionService(service); + enforceIsCurrentVoiceInteractionService(); if (callback == null || recognitionConfig == null || bcp47Locale == null) { throw new IllegalArgumentException("Illegal argument(s) in startRecognition"); @@ -1117,11 +1112,10 @@ public class VoiceInteractionManagerService extends SystemService { } @Override - public int stopRecognition(IVoiceInteractionService service, int keyphraseId, - IRecognitionStatusCallback callback) { + public int stopRecognition(int keyphraseId, IRecognitionStatusCallback callback) { // Allow the call if this is the current voice interaction service. synchronized (this) { - enforceIsCurrentVoiceInteractionService(service); + enforceIsCurrentVoiceInteractionService(); } final long caller = Binder.clearCallingIdentity(); @@ -1133,11 +1127,10 @@ public class VoiceInteractionManagerService extends SystemService { } @Override - public int setParameter(IVoiceInteractionService service, int keyphraseId, - @ModelParams int modelParam, int value) { + public int setParameter(int keyphraseId, @ModelParams int modelParam, int value) { // Allow the call if this is the current voice interaction service. synchronized (this) { - enforceIsCurrentVoiceInteractionService(service); + enforceIsCurrentVoiceInteractionService(); } final long caller = Binder.clearCallingIdentity(); @@ -1149,11 +1142,10 @@ public class VoiceInteractionManagerService extends SystemService { } @Override - public int getParameter(IVoiceInteractionService service, int keyphraseId, - @ModelParams int modelParam) { + public int getParameter(int keyphraseId, @ModelParams int modelParam) { // Allow the call if this is the current voice interaction service. synchronized (this) { - enforceIsCurrentVoiceInteractionService(service); + enforceIsCurrentVoiceInteractionService(); } final long caller = Binder.clearCallingIdentity(); @@ -1166,11 +1158,10 @@ public class VoiceInteractionManagerService extends SystemService { @Override @Nullable - public ModelParamRange queryParameter(IVoiceInteractionService service, - int keyphraseId, @ModelParams int modelParam) { + public ModelParamRange queryParameter(int keyphraseId, @ModelParams int modelParam) { // Allow the call if this is the current voice interaction service. synchronized (this) { - enforceIsCurrentVoiceInteractionService(service); + enforceIsCurrentVoiceInteractionService(); } final long caller = Binder.clearCallingIdentity(); @@ -1400,9 +1391,9 @@ public class VoiceInteractionManagerService extends SystemService { } @Override - public void setUiHints(IVoiceInteractionService service, Bundle hints) { + public void setUiHints(Bundle hints) { synchronized (this) { - enforceIsCurrentVoiceInteractionService(service); + enforceIsCurrentVoiceInteractionService(); final int size = mVoiceInteractionSessionListeners.beginBroadcast(); for (int i = 0; i < size; ++i) { @@ -1425,14 +1416,32 @@ public class VoiceInteractionManagerService extends SystemService { } } - private void enforceIsCurrentVoiceInteractionService(IVoiceInteractionService service) { - if (mImpl == null || mImpl.mService == null - || service.asBinder() != mImpl.mService.asBinder()) { + private void enforceIsCurrentVoiceInteractionService() { + if (!isCallerCurrentVoiceInteractionService()) { throw new SecurityException("Caller is not the current voice interaction service"); } } + private void enforceCallerAllowedToEnrollVoiceModel() { + enforceCallingPermission(Manifest.permission.MANAGE_VOICE_KEYPHRASES); + if (!isCallerCurrentVoiceInteractionService() + && !isCallerTrustedEnrollmentApplication()) { + throw new SecurityException("Caller is required to be the current voice interaction" + + " service or a system enrollment application to enroll voice models"); + } + } + + private boolean isCallerCurrentVoiceInteractionService() { + return mImpl != null + && mImpl.mInfo.getServiceInfo().applicationInfo.uid == Binder.getCallingUid(); + } + + private boolean isCallerTrustedEnrollmentApplication() { + return mImpl.mEnrollmentApplicationInfo.isUidSupportedEnrollmentApplication( + Binder.getCallingUid()); + } + private void setImplLocked(VoiceInteractionManagerServiceImpl impl) { mImpl = impl; mAtmInternal.notifyActiveVoiceInteractionServiceChanged( diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java index a62b03ca82e4..b813f87f335f 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java @@ -36,6 +36,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; import android.content.pm.PackageManager; +import android.hardware.soundtrigger.KeyphraseEnrollmentInfo; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; @@ -78,6 +79,7 @@ class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConne final IActivityManager mAm; final IActivityTaskManager mAtm; final VoiceInteractionServiceInfo mInfo; + final KeyphraseEnrollmentInfo mEnrollmentApplicationInfo; final ComponentName mSessionComponentName; final IWindowManager mIWindowManager; boolean mBound = false; @@ -133,6 +135,7 @@ class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConne mComponent = service; mAm = ActivityManager.getService(); mAtm = ActivityTaskManager.getService(); + mEnrollmentApplicationInfo = new KeyphraseEnrollmentInfo(context.getPackageManager()); VoiceInteractionServiceInfo info; try { info = new VoiceInteractionServiceInfo(context.getPackageManager(), service, mUser); @@ -403,6 +406,7 @@ class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConne pw.println(" Active session:"); mActiveSession.dump(" ", pw); } + pw.println(" " + mEnrollmentApplicationInfo.toString()); } void startLocked() { |