1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.media.voice;
18 
19 import android.Manifest;
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.annotation.RequiresPermission;
23 import android.annotation.SystemApi;
24 import android.annotation.TestApi;
25 import android.hardware.soundtrigger.SoundTrigger;
26 import android.os.Binder;
27 import android.os.RemoteException;
28 import android.os.ServiceSpecificException;
29 import android.util.Slog;
30 
31 import com.android.internal.app.IVoiceInteractionManagerService;
32 
33 import java.util.Locale;
34 import java.util.Objects;
35 
36 /**
37  * This class provides management of voice based sound recognition models. Usage of this class is
38  * restricted to system or signature applications only. This allows OEMs to write apps that can
39  * manage voice based sound trigger models.
40  * Callers of this class are expected to have whitelist manifest permission MANAGE_VOICE_KEYPHRASES.
41  * Callers of this class are expected to be the designated voice interaction service via
42  * {@link Settings.Secure.VOICE_INTERACTION_SERVICE} or a bundled voice model enrollment application
43  * detected by {@link android.hardware.soundtrigger.KeyphraseEnrollmentInfo}.
44  * @hide
45  */
46 @SystemApi
47 public final class KeyphraseModelManager {
48     private static final boolean DBG = false;
49     private static final String TAG = "KeyphraseModelManager";
50 
51     private final IVoiceInteractionManagerService mVoiceInteractionManagerService;
52 
53     /**
54      * @hide
55      */
KeyphraseModelManager( IVoiceInteractionManagerService voiceInteractionManagerService)56     public KeyphraseModelManager(
57             IVoiceInteractionManagerService voiceInteractionManagerService) {
58         if (DBG) {
59             Slog.i(TAG, "KeyphraseModelManager created.");
60         }
61         mVoiceInteractionManagerService = voiceInteractionManagerService;
62     }
63 
64 
65     /**
66      * Gets the registered sound model for keyphrase detection for the current user.
67      * The keyphraseId and locale passed must match a supported model passed in via
68      * {@link #updateKeyphraseSoundModel}.
69      * If the active voice interaction service changes from the current user, all requests will be
70      * rejected, and any registered models will be unregistered.
71      * Caller must either be the active voice interaction service via
72      * {@link Settings.Secure.VOICE_INTERACTION_SERVICE}, or the caller must be a voice model
73      * enrollment application detected by
74      * {@link android.hardware.soundtrigger.KeyphraseEnrollmentInfo}.
75      *
76      * @param keyphraseId The unique identifier for the keyphrase.
77      * @param locale The locale language tag supported by the desired model.
78      * @return Registered keyphrase sound model matching the keyphrase ID and locale. May be null if
79      * no matching sound model exists.
80      * @throws SecurityException Thrown when caller does not have MANAGE_VOICE_KEYPHRASES permission
81      *                           or if the caller is not the active voice interaction service.
82      */
83     @RequiresPermission(Manifest.permission.MANAGE_VOICE_KEYPHRASES)
84     @Nullable
getKeyphraseSoundModel(int keyphraseId, @NonNull Locale locale)85     public SoundTrigger.KeyphraseSoundModel getKeyphraseSoundModel(int keyphraseId,
86             @NonNull Locale locale) {
87         Objects.requireNonNull(locale);
88         try {
89             return mVoiceInteractionManagerService.getKeyphraseSoundModel(keyphraseId,
90                     locale.toLanguageTag());
91         } catch (RemoteException e) {
92             throw e.rethrowFromSystemServer();
93         }
94     }
95 
96     /**
97      * Add or update the given keyphrase sound model to the registered models pool for the current
98      * user.
99      * If a model exists with the same Keyphrase ID, locale, and user list. The registered model
100      * will be overwritten with the new model.
101      * If the active voice interaction service changes from the current user, all requests will be
102      * rejected, and any registered models will be unregistered.
103      * Caller must either be the active voice interaction service via
104      * {@link Settings.Secure.VOICE_INTERACTION_SERVICE}, or the caller must be a voice model
105      * enrollment application detected by
106      * {@link android.hardware.soundtrigger.KeyphraseEnrollmentInfo}.
107      *
108      * @param model Keyphrase sound model to be updated.
109      * @throws ServiceSpecificException Thrown with error code if failed to update the keyphrase
110      *                           sound model.
111      * @throws SecurityException Thrown when caller does not have MANAGE_VOICE_KEYPHRASES permission
112      *                           or if the caller is not the active voice interaction service.
113      */
114     @RequiresPermission(Manifest.permission.MANAGE_VOICE_KEYPHRASES)
updateKeyphraseSoundModel(@onNull SoundTrigger.KeyphraseSoundModel model)115     public void updateKeyphraseSoundModel(@NonNull SoundTrigger.KeyphraseSoundModel model) {
116         Objects.requireNonNull(model);
117         try {
118             int status = mVoiceInteractionManagerService.updateKeyphraseSoundModel(model);
119             if (status != SoundTrigger.STATUS_OK) {
120                 throw new ServiceSpecificException(status);
121             }
122         } catch (RemoteException e) {
123             throw e.rethrowFromSystemServer();
124         }
125     }
126 
127     /**
128      * Delete keyphrase sound model from the registered models pool for the current user matching\
129      * the keyphrase ID and locale.
130      * The keyphraseId and locale passed must match a supported model passed in via
131      * {@link #updateKeyphraseSoundModel}.
132      * If the active voice interaction service changes from the current user, all requests will be
133      * rejected, and any registered models will be unregistered.
134      * Caller must either be the active voice interaction service via
135      * {@link Settings.Secure.VOICE_INTERACTION_SERVICE}, or the caller must be a voice model
136      * enrollment application detected by
137      * {@link android.hardware.soundtrigger.KeyphraseEnrollmentInfo}.
138      *
139      * @param keyphraseId The unique identifier for the keyphrase.
140      * @param locale The locale language tag supported by the desired model.
141      * @throws ServiceSpecificException Thrown with error code if failed to delete the keyphrase
142      *                           sound model.
143      * @throws SecurityException Thrown when caller does not have MANAGE_VOICE_KEYPHRASES permission
144      *                           or if the caller is not the active voice interaction service.
145      */
146     @RequiresPermission(Manifest.permission.MANAGE_VOICE_KEYPHRASES)
deleteKeyphraseSoundModel(int keyphraseId, @NonNull Locale locale)147     public void deleteKeyphraseSoundModel(int keyphraseId, @NonNull Locale locale) {
148         Objects.requireNonNull(locale);
149         try {
150             int status = mVoiceInteractionManagerService.deleteKeyphraseSoundModel(keyphraseId,
151                     locale.toLanguageTag());
152             if (status != SoundTrigger.STATUS_OK) {
153                 throw new ServiceSpecificException(status);
154             }
155         } catch (RemoteException e) {
156             throw e.rethrowFromSystemServer();
157         }
158     }
159 
160     /**
161      * Override the persistent enrolled model database with an in-memory
162      * fake for testing purposes.
163      *
164      * @param enabled - {@code true} if the model enrollment database should be overridden with an
165      * in-memory fake. {@code false} if the real, persistent model enrollment database should be
166      * used.
167      * @hide
168      */
169     @RequiresPermission(Manifest.permission.MANAGE_VOICE_KEYPHRASES)
170     @TestApi
setModelDatabaseForTestEnabled(boolean enabled)171     public void setModelDatabaseForTestEnabled(boolean enabled) {
172         try {
173             mVoiceInteractionManagerService.setModelDatabaseForTestEnabled(enabled, new Binder());
174         } catch (RemoteException e) {
175             throw e.rethrowFromSystemServer();
176         }
177     }
178 }
179