VoiceInteractionManagerService.java revision 6daae9622672e0b38fc2efed29f68061d749cacc
1/* 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.voiceinteraction; 18 19import android.Manifest; 20import android.app.ActivityManager; 21import android.content.ComponentName; 22import android.content.ContentResolver; 23import android.content.Context; 24import android.content.Intent; 25import android.content.pm.PackageManager; 26import android.database.ContentObserver; 27import android.hardware.soundtrigger.IRecognitionStatusCallback; 28import android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel; 29import android.hardware.soundtrigger.SoundTrigger.ModuleProperties; 30import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig; 31import android.os.Binder; 32import android.os.Bundle; 33import android.os.Handler; 34import android.os.IBinder; 35import android.os.Parcel; 36import android.os.RemoteException; 37import android.os.UserHandle; 38import android.provider.Settings; 39import android.service.voice.IVoiceInteractionService; 40import android.service.voice.IVoiceInteractionSession; 41import android.util.Slog; 42 43import com.android.internal.app.IVoiceInteractionManagerService; 44import com.android.internal.app.IVoiceInteractor; 45import com.android.internal.content.PackageMonitor; 46import com.android.internal.os.BackgroundThread; 47import com.android.server.SystemService; 48import com.android.server.UiThread; 49 50import java.io.FileDescriptor; 51import java.io.PrintWriter; 52import java.util.List; 53 54 55/** 56 * SystemService that publishes an IVoiceInteractionManagerService. 57 */ 58public class VoiceInteractionManagerService extends SystemService { 59 60 static final String TAG = "VoiceInteractionManagerService"; 61 62 final Context mContext; 63 final ContentResolver mResolver; 64 final DatabaseHelper mDbHelper; 65 final SoundTriggerHelper mSoundTriggerHelper; 66 67 public VoiceInteractionManagerService(Context context) { 68 super(context); 69 mContext = context; 70 mResolver = context.getContentResolver(); 71 mDbHelper = new DatabaseHelper(context); 72 mSoundTriggerHelper = new SoundTriggerHelper(); 73 } 74 75 @Override 76 public void onStart() { 77 publishBinderService(Context.VOICE_INTERACTION_MANAGER_SERVICE, mServiceStub); 78 } 79 80 @Override 81 public void onBootPhase(int phase) { 82 if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) { 83 mServiceStub.systemRunning(isSafeMode()); 84 } 85 } 86 87 @Override 88 public void onSwitchUser(int userHandle) { 89 mServiceStub.switchUser(userHandle); 90 } 91 92 // implementation entry point and binder service 93 private final VoiceInteractionManagerServiceStub mServiceStub 94 = new VoiceInteractionManagerServiceStub(); 95 96 class VoiceInteractionManagerServiceStub extends IVoiceInteractionManagerService.Stub { 97 98 VoiceInteractionManagerServiceImpl mImpl; 99 100 private boolean mSafeMode; 101 private int mCurUser; 102 103 @Override 104 public boolean onTransact(int code, Parcel data, Parcel reply, int flags) 105 throws RemoteException { 106 try { 107 return super.onTransact(code, data, reply, flags); 108 } catch (RuntimeException e) { 109 // The activity manager only throws security exceptions, so let's 110 // log all others. 111 if (!(e instanceof SecurityException)) { 112 Slog.wtf(TAG, "VoiceInteractionManagerService Crash", e); 113 } 114 throw e; 115 } 116 } 117 118 public void systemRunning(boolean safeMode) { 119 mSafeMode = safeMode; 120 121 mPackageMonitor.register(mContext, BackgroundThread.getHandler().getLooper(), 122 UserHandle.ALL, true); 123 new SettingsObserver(UiThread.getHandler()); 124 125 synchronized (this) { 126 mCurUser = ActivityManager.getCurrentUser(); 127 switchImplementationIfNeededLocked(false); 128 } 129 } 130 131 public void switchUser(int userHandle) { 132 synchronized (this) { 133 mCurUser = userHandle; 134 switchImplementationIfNeededLocked(false); 135 } 136 } 137 138 void switchImplementationIfNeededLocked(boolean force) { 139 if (!mSafeMode) { 140 String curService = Settings.Secure.getStringForUser( 141 mResolver, Settings.Secure.VOICE_INTERACTION_SERVICE, mCurUser); 142 ComponentName serviceComponent = null; 143 if (curService != null && !curService.isEmpty()) { 144 try { 145 serviceComponent = ComponentName.unflattenFromString(curService); 146 } catch (RuntimeException e) { 147 Slog.wtf(TAG, "Bad voice interaction service name " + curService, e); 148 serviceComponent = null; 149 } 150 } 151 if (force || mImpl == null || mImpl.mUser != mCurUser 152 || !mImpl.mComponent.equals(serviceComponent)) { 153 if (mImpl != null) { 154 mImpl.shutdownLocked(); 155 } 156 if (serviceComponent != null) { 157 mImpl = new VoiceInteractionManagerServiceImpl(mContext, 158 UiThread.getHandler(), this, mCurUser, serviceComponent); 159 mImpl.startLocked(); 160 } else { 161 mImpl = null; 162 } 163 } 164 } 165 } 166 167 @Override 168 public void startSession(IVoiceInteractionService service, Bundle args) { 169 synchronized (this) { 170 if (mImpl == null || mImpl.mService == null 171 || service.asBinder() != mImpl.mService.asBinder()) { 172 throw new SecurityException( 173 "Caller is not the current voice interaction service"); 174 } 175 final int callingPid = Binder.getCallingPid(); 176 final int callingUid = Binder.getCallingUid(); 177 final long caller = Binder.clearCallingIdentity(); 178 try { 179 mImpl.startSessionLocked(callingPid, callingUid, args); 180 } finally { 181 Binder.restoreCallingIdentity(caller); 182 } 183 } 184 } 185 186 @Override 187 public boolean deliverNewSession(IBinder token, IVoiceInteractionSession session, 188 IVoiceInteractor interactor) { 189 synchronized (this) { 190 if (mImpl == null) { 191 throw new SecurityException( 192 "deliverNewSession without running voice interaction service"); 193 } 194 final int callingPid = Binder.getCallingPid(); 195 final int callingUid = Binder.getCallingUid(); 196 final long caller = Binder.clearCallingIdentity(); 197 try { 198 return mImpl.deliverNewSessionLocked(callingPid, callingUid, token, session, 199 interactor); 200 } finally { 201 Binder.restoreCallingIdentity(caller); 202 } 203 } 204 } 205 206 @Override 207 public int startVoiceActivity(IBinder token, Intent intent, String resolvedType) { 208 synchronized (this) { 209 if (mImpl == null) { 210 Slog.w(TAG, "startVoiceActivity without running voice interaction service"); 211 return ActivityManager.START_CANCELED; 212 } 213 final int callingPid = Binder.getCallingPid(); 214 final int callingUid = Binder.getCallingUid(); 215 final long caller = Binder.clearCallingIdentity(); 216 try { 217 return mImpl.startVoiceActivityLocked(callingPid, callingUid, token, 218 intent, resolvedType); 219 } finally { 220 Binder.restoreCallingIdentity(caller); 221 } 222 } 223 } 224 225 @Override 226 public void finish(IBinder token) { 227 synchronized (this) { 228 if (mImpl == null) { 229 Slog.w(TAG, "finish without running voice interaction service"); 230 return; 231 } 232 final int callingPid = Binder.getCallingPid(); 233 final int callingUid = Binder.getCallingUid(); 234 final long caller = Binder.clearCallingIdentity(); 235 try { 236 mImpl.finishLocked(callingPid, callingUid, token); 237 } finally { 238 Binder.restoreCallingIdentity(caller); 239 } 240 } 241 } 242 243 //----------------- Model management APIs --------------------------------// 244 245 @Override 246 public List<KeyphraseSoundModel> listRegisteredKeyphraseSoundModels( 247 IVoiceInteractionService service) { 248 // Allow the call if this is the current voice interaction service 249 // or the caller holds the MANAGE_VOICE_KEYPHRASES permission. 250 synchronized (this) { 251 boolean permissionGranted = 252 mContext.checkCallingPermission(Manifest.permission.MANAGE_VOICE_KEYPHRASES) 253 == PackageManager.PERMISSION_GRANTED; 254 boolean currentVoiceInteractionService = service != null 255 && mImpl != null 256 && mImpl.mService != null 257 && service.asBinder() == mImpl.mService.asBinder(); 258 259 if (!permissionGranted && !currentVoiceInteractionService) { 260 if (!currentVoiceInteractionService) { 261 throw new SecurityException( 262 "Caller is not the current voice interaction service"); 263 } 264 if (!permissionGranted) { 265 throw new SecurityException("Caller does not hold the permission " 266 + Manifest.permission.MANAGE_VOICE_KEYPHRASES); 267 } 268 } 269 270 final long caller = Binder.clearCallingIdentity(); 271 try { 272 return mDbHelper.getKephraseSoundModels(); 273 } finally { 274 Binder.restoreCallingIdentity(caller); 275 } 276 } 277 } 278 279 @Override 280 public int updateKeyphraseSoundModel(KeyphraseSoundModel model) { 281 synchronized (this) { 282 if (mContext.checkCallingPermission(Manifest.permission.MANAGE_VOICE_KEYPHRASES) 283 != PackageManager.PERMISSION_GRANTED) { 284 throw new SecurityException("Caller does not hold the permission " 285 + Manifest.permission.MANAGE_VOICE_KEYPHRASES); 286 } 287 if (model == null) { 288 throw new IllegalArgumentException("Model must not be null"); 289 } 290 291 final long caller = Binder.clearCallingIdentity(); 292 try { 293 boolean success = false; 294 if (model.keyphrases == null) { 295 // If the keyphrases are not present in the model, delete the model. 296 success = mDbHelper.deleteKeyphraseSoundModel(model.uuid); 297 } else { 298 // Else update the model. 299 success = mDbHelper.addOrUpdateKeyphraseSoundModel(model); 300 } 301 if (success) { 302 // Notify the voice interaction service of a change in sound models. 303 if (mImpl != null && mImpl.mService != null) { 304 mImpl.notifySoundModelsChangedLocked(); 305 } 306 return SoundTriggerHelper.STATUS_OK; 307 } else { 308 return SoundTriggerHelper.STATUS_ERROR; 309 } 310 } finally { 311 Binder.restoreCallingIdentity(caller); 312 } 313 } 314 } 315 316 //----------------- SoundTrigger APIs --------------------------------// 317 @Override 318 public ModuleProperties getDspModuleProperties(IVoiceInteractionService service) { 319 // Allow the call if this is the current voice interaction service. 320 synchronized (this) { 321 if (mImpl == null || mImpl.mService == null 322 || service == null || service.asBinder() != mImpl.mService.asBinder()) { 323 throw new SecurityException( 324 "Caller is not the current voice interaction service"); 325 } 326 327 final long caller = Binder.clearCallingIdentity(); 328 try { 329 return mSoundTriggerHelper.moduleProperties; 330 } finally { 331 Binder.restoreCallingIdentity(caller); 332 } 333 } 334 } 335 336 @Override 337 public int startRecognition(IVoiceInteractionService service, int keyphraseId, 338 KeyphraseSoundModel soundModel, IRecognitionStatusCallback callback, 339 RecognitionConfig recognitionConfig) { 340 // Allow the call if this is the current voice interaction service. 341 synchronized (this) { 342 if (mImpl == null || mImpl.mService == null 343 || service == null || service.asBinder() != mImpl.mService.asBinder()) { 344 throw new SecurityException( 345 "Caller is not the current voice interaction service"); 346 } 347 348 final long caller = Binder.clearCallingIdentity(); 349 try { 350 return mSoundTriggerHelper.startRecognition( 351 keyphraseId, soundModel, callback, recognitionConfig); 352 } finally { 353 Binder.restoreCallingIdentity(caller); 354 } 355 } 356 } 357 358 @Override 359 public int stopRecognition(IVoiceInteractionService service, int keyphraseId, 360 IRecognitionStatusCallback callback) { 361 // Allow the call if this is the current voice interaction service. 362 synchronized (this) { 363 if (mImpl == null || mImpl.mService == null 364 || service == null || service.asBinder() != mImpl.mService.asBinder()) { 365 throw new SecurityException( 366 "Caller is not the current voice interaction service"); 367 } 368 369 final long caller = Binder.clearCallingIdentity(); 370 try { 371 return mSoundTriggerHelper.stopRecognition(keyphraseId, callback); 372 } finally { 373 Binder.restoreCallingIdentity(caller); 374 } 375 } 376 } 377 378 @Override 379 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 380 if (mContext.checkCallingOrSelfPermission(Manifest.permission.DUMP) 381 != PackageManager.PERMISSION_GRANTED) { 382 pw.println("Permission Denial: can't dump PowerManager from from pid=" 383 + Binder.getCallingPid() 384 + ", uid=" + Binder.getCallingUid()); 385 return; 386 } 387 synchronized (this) { 388 pw.println("VOICE INTERACTION MANAGER (dumpsys voiceinteraction)\n"); 389 if (mImpl == null) { 390 pw.println(" (No active implementation)"); 391 return; 392 } 393 mImpl.dumpLocked(fd, pw, args); 394 } 395 } 396 397 class SettingsObserver extends ContentObserver { 398 SettingsObserver(Handler handler) { 399 super(handler); 400 ContentResolver resolver = mContext.getContentResolver(); 401 resolver.registerContentObserver(Settings.Secure.getUriFor( 402 Settings.Secure.VOICE_INTERACTION_SERVICE), false, this); 403 } 404 405 @Override public void onChange(boolean selfChange) { 406 synchronized (VoiceInteractionManagerServiceStub.this) { 407 switchImplementationIfNeededLocked(false); 408 } 409 } 410 } 411 412 PackageMonitor mPackageMonitor = new PackageMonitor() { 413 @Override 414 public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) { 415 return super.onHandleForceStop(intent, packages, uid, doit); 416 } 417 418 @Override 419 public void onHandleUserStop(Intent intent, int userHandle) { 420 } 421 422 @Override 423 public void onPackageDisappeared(String packageName, int reason) { 424 } 425 426 @Override 427 public void onPackageAppeared(String packageName, int reason) { 428 if (mImpl != null && packageName.equals(mImpl.mComponent.getPackageName())) { 429 switchImplementationIfNeededLocked(true); 430 } 431 } 432 433 @Override 434 public void onPackageModified(String packageName) { 435 } 436 437 @Override 438 public void onSomePackagesChanged() { 439 } 440 }; 441 } 442} 443