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