blob: a4c1210ba18886b9b34ee1666bc631a3fae2e097 [file] [log] [blame]
Arunesh Mishraa772e5f2016-01-25 10:33:11 -08001/*
2 * Copyright (C) 2014 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
17package com.android.server.soundtrigger;
Arunesh Mishra3fff7f52016-02-09 12:15:19 -080018import static android.hardware.soundtrigger.SoundTrigger.STATUS_ERROR;
Arunesh Mishraa772e5f2016-01-25 10:33:11 -080019
20import android.content.Context;
21import android.content.pm.PackageManager;
22import android.Manifest;
23import android.hardware.soundtrigger.IRecognitionStatusCallback;
24import android.hardware.soundtrigger.SoundTrigger;
Arunesh Mishrac722ec412016-01-27 13:29:12 -080025import android.hardware.soundtrigger.SoundTrigger.GenericSoundModel;
Arunesh Mishraa772e5f2016-01-25 10:33:11 -080026import android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel;
Arunesh Mishra55a9b002016-02-01 14:06:37 -080027import android.hardware.soundtrigger.SoundTrigger.ModuleProperties;
Arunesh Mishraa772e5f2016-01-25 10:33:11 -080028import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig;
Arunesh Mishraa772e5f2016-01-25 10:33:11 -080029import android.os.Parcel;
30import android.os.ParcelUuid;
31import android.os.RemoteException;
32import android.util.Slog;
33
34import com.android.server.SystemService;
35import com.android.internal.app.ISoundTriggerService;
36
37import java.io.FileDescriptor;
38import java.io.PrintWriter;
39import java.util.UUID;
40
41/**
42 * A single SystemService to manage all sound/voice-based sound models on the DSP.
43 * This services provides apis to manage sound trigger-based sound models via
44 * the ISoundTriggerService interface. This class also publishes a local interface encapsulating
45 * the functionality provided by {@link SoundTriggerHelper} for use by
46 * {@link VoiceInteractionManagerService}.
47 *
48 * @hide
49 */
50public class SoundTriggerService extends SystemService {
Arunesh Mishra3fff7f52016-02-09 12:15:19 -080051 private static final String TAG = "SoundTriggerService";
52 private static final boolean DEBUG = true;
Arunesh Mishraa772e5f2016-01-25 10:33:11 -080053
54 final Context mContext;
55 private final SoundTriggerServiceStub mServiceStub;
56 private final LocalSoundTriggerService mLocalSoundTriggerService;
57 private SoundTriggerDbHelper mDbHelper;
Arunesh Mishra3fff7f52016-02-09 12:15:19 -080058 private SoundTriggerHelper mSoundTriggerHelper;
Arunesh Mishraa772e5f2016-01-25 10:33:11 -080059
60 public SoundTriggerService(Context context) {
61 super(context);
62 mContext = context;
63 mServiceStub = new SoundTriggerServiceStub();
64 mLocalSoundTriggerService = new LocalSoundTriggerService(context);
65 }
66
67 @Override
68 public void onStart() {
69 publishBinderService(Context.SOUND_TRIGGER_SERVICE, mServiceStub);
70 publishLocalService(SoundTriggerInternal.class, mLocalSoundTriggerService);
71 }
72
73 @Override
74 public void onBootPhase(int phase) {
75 if (PHASE_SYSTEM_SERVICES_READY == phase) {
Arunesh Mishra3fff7f52016-02-09 12:15:19 -080076 initSoundTriggerHelper();
77 mLocalSoundTriggerService.setSoundTriggerHelper(mSoundTriggerHelper);
Arunesh Mishraa772e5f2016-01-25 10:33:11 -080078 } else if (PHASE_THIRD_PARTY_APPS_CAN_START == phase) {
79 mDbHelper = new SoundTriggerDbHelper(mContext);
80 }
81 }
82
83 @Override
84 public void onStartUser(int userHandle) {
85 }
86
87 @Override
88 public void onSwitchUser(int userHandle) {
89 }
90
Arunesh Mishra3fff7f52016-02-09 12:15:19 -080091 private synchronized void initSoundTriggerHelper() {
92 if (mSoundTriggerHelper == null) {
93 mSoundTriggerHelper = new SoundTriggerHelper(mContext);
94 }
95 }
96
97 private synchronized boolean isInitialized() {
98 if (mSoundTriggerHelper == null ) {
99 Slog.e(TAG, "SoundTriggerHelper not initialized.");
100 return false;
101 }
102 return true;
103 }
104
Arunesh Mishraa772e5f2016-01-25 10:33:11 -0800105 class SoundTriggerServiceStub extends ISoundTriggerService.Stub {
106 @Override
107 public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
108 throws RemoteException {
109 try {
110 return super.onTransact(code, data, reply, flags);
111 } catch (RuntimeException e) {
112 // The activity manager only throws security exceptions, so let's
113 // log all others.
114 if (!(e instanceof SecurityException)) {
115 Slog.wtf(TAG, "SoundTriggerService Crash", e);
116 }
117 throw e;
118 }
119 }
120
121 @Override
Arunesh Mishra3fff7f52016-02-09 12:15:19 -0800122 public int startRecognition(ParcelUuid parcelUuid, IRecognitionStatusCallback callback,
123 RecognitionConfig config) {
Arunesh Mishraa772e5f2016-01-25 10:33:11 -0800124 enforceCallingPermission(Manifest.permission.MANAGE_SOUND_TRIGGER);
Arunesh Mishra2d1de782016-02-21 18:10:28 -0800125 if (!isInitialized()) return STATUS_ERROR;
Arunesh Mishraa772e5f2016-01-25 10:33:11 -0800126 if (DEBUG) {
127 Slog.i(TAG, "startRecognition(): Uuid : " + parcelUuid);
128 }
Arunesh Mishra3fff7f52016-02-09 12:15:19 -0800129
130 GenericSoundModel model = getSoundModel(parcelUuid);
131 if (model == null) {
132 Slog.e(TAG, "Null model in database for id: " + parcelUuid);
133 return STATUS_ERROR;
134 }
135
136 return mSoundTriggerHelper.startGenericRecognition(parcelUuid.getUuid(), model,
137 callback, config);
Arunesh Mishraa772e5f2016-01-25 10:33:11 -0800138 }
139
140 @Override
Arunesh Mishra3fff7f52016-02-09 12:15:19 -0800141 public int stopRecognition(ParcelUuid parcelUuid, IRecognitionStatusCallback callback) {
Arunesh Mishraa772e5f2016-01-25 10:33:11 -0800142 enforceCallingPermission(Manifest.permission.MANAGE_SOUND_TRIGGER);
143 if (DEBUG) {
144 Slog.i(TAG, "stopRecognition(): Uuid : " + parcelUuid);
145 }
Arunesh Mishra3fff7f52016-02-09 12:15:19 -0800146 if (!isInitialized()) return STATUS_ERROR;
147 return mSoundTriggerHelper.stopGenericRecognition(parcelUuid.getUuid(), callback);
Arunesh Mishraa772e5f2016-01-25 10:33:11 -0800148 }
149
150 @Override
Arunesh Mishrac722ec412016-01-27 13:29:12 -0800151 public SoundTrigger.GenericSoundModel getSoundModel(ParcelUuid soundModelId) {
Arunesh Mishraa772e5f2016-01-25 10:33:11 -0800152 enforceCallingPermission(Manifest.permission.MANAGE_SOUND_TRIGGER);
153 if (DEBUG) {
154 Slog.i(TAG, "getSoundModel(): id = " + soundModelId);
155 }
Arunesh Mishra3fff7f52016-02-09 12:15:19 -0800156 SoundTrigger.GenericSoundModel model = mDbHelper.getGenericSoundModel(
157 soundModelId.getUuid());
Arunesh Mishraa772e5f2016-01-25 10:33:11 -0800158 return model;
159 }
160
161 @Override
Arunesh Mishrac722ec412016-01-27 13:29:12 -0800162 public void updateSoundModel(SoundTrigger.GenericSoundModel soundModel) {
Arunesh Mishraa772e5f2016-01-25 10:33:11 -0800163 enforceCallingPermission(Manifest.permission.MANAGE_SOUND_TRIGGER);
164 if (DEBUG) {
165 Slog.i(TAG, "updateSoundModel(): model = " + soundModel);
166 }
Arunesh Mishrac722ec412016-01-27 13:29:12 -0800167 mDbHelper.updateGenericSoundModel(soundModel);
Arunesh Mishraa772e5f2016-01-25 10:33:11 -0800168 }
169
170 @Override
171 public void deleteSoundModel(ParcelUuid soundModelId) {
172 enforceCallingPermission(Manifest.permission.MANAGE_SOUND_TRIGGER);
173 if (DEBUG) {
174 Slog.i(TAG, "deleteSoundModel(): id = " + soundModelId);
175 }
Arunesh Mishra2d1de782016-02-21 18:10:28 -0800176 // Unload the model if it is loaded.
177 mSoundTriggerHelper.unloadGenericSoundModel(soundModelId.getUuid());
Arunesh Mishrac722ec412016-01-27 13:29:12 -0800178 mDbHelper.deleteGenericSoundModel(soundModelId.getUuid());
Arunesh Mishraa772e5f2016-01-25 10:33:11 -0800179 }
180 }
181
182 public final class LocalSoundTriggerService extends SoundTriggerInternal {
183 private final Context mContext;
184 private SoundTriggerHelper mSoundTriggerHelper;
185
186 LocalSoundTriggerService(Context context) {
187 mContext = context;
188 }
189
Arunesh Mishra3fff7f52016-02-09 12:15:19 -0800190 synchronized void setSoundTriggerHelper(SoundTriggerHelper helper) {
191 mSoundTriggerHelper = helper;
Arunesh Mishraa772e5f2016-01-25 10:33:11 -0800192 }
193
194 @Override
195 public int startRecognition(int keyphraseId, KeyphraseSoundModel soundModel,
196 IRecognitionStatusCallback listener, RecognitionConfig recognitionConfig) {
Arunesh Mishra3fff7f52016-02-09 12:15:19 -0800197 if (!isInitialized()) return STATUS_ERROR;
198 return mSoundTriggerHelper.startKeyphraseRecognition(keyphraseId, soundModel, listener,
Arunesh Mishraa772e5f2016-01-25 10:33:11 -0800199 recognitionConfig);
200 }
201
202 @Override
Arunesh Mishra3fff7f52016-02-09 12:15:19 -0800203 public synchronized int stopRecognition(int keyphraseId, IRecognitionStatusCallback listener) {
204 if (!isInitialized()) return STATUS_ERROR;
205 return mSoundTriggerHelper.stopKeyphraseRecognition(keyphraseId, listener);
Arunesh Mishraa772e5f2016-01-25 10:33:11 -0800206 }
207
208 @Override
209 public void stopAllRecognitions() {
Arunesh Mishra3fff7f52016-02-09 12:15:19 -0800210 if (!isInitialized()) return;
Arunesh Mishraa772e5f2016-01-25 10:33:11 -0800211 mSoundTriggerHelper.stopAllRecognitions();
212 }
213
214 @Override
Arunesh Mishra55a9b002016-02-01 14:06:37 -0800215 public ModuleProperties getModuleProperties() {
Arunesh Mishra3fff7f52016-02-09 12:15:19 -0800216 if (!isInitialized()) return null;
Arunesh Mishra55a9b002016-02-01 14:06:37 -0800217 return mSoundTriggerHelper.getModuleProperties();
218 }
219
220 @Override
Arunesh Mishra2d1de782016-02-21 18:10:28 -0800221 public int unloadKeyphraseModel(int keyphraseId) {
222 if (!isInitialized()) return STATUS_ERROR;
223 return mSoundTriggerHelper.unloadKeyphraseSoundModel(keyphraseId);
224 }
225
226 @Override
Arunesh Mishraa772e5f2016-01-25 10:33:11 -0800227 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Arunesh Mishra3fff7f52016-02-09 12:15:19 -0800228 if (!isInitialized()) return;
Arunesh Mishraa772e5f2016-01-25 10:33:11 -0800229 mSoundTriggerHelper.dump(fd, pw, args);
230 }
Arunesh Mishra3fff7f52016-02-09 12:15:19 -0800231
232 private synchronized boolean isInitialized() {
233 if (mSoundTriggerHelper == null ) {
234 Slog.e(TAG, "SoundTriggerHelper not initialized.");
235 return false;
236 }
237 return true;
238 }
Arunesh Mishraa772e5f2016-01-25 10:33:11 -0800239 }
240
241 private void enforceCallingPermission(String permission) {
242 if (mContext.checkCallingOrSelfPermission(permission)
243 != PackageManager.PERMISSION_GRANTED) {
244 throw new SecurityException("Caller does not hold the permission " + permission);
245 }
246 }
247}