VoiceInteractionManagerService.java revision 5f560d056243638a83a4e7770035fdb66004c9d7
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.app.AppGlobals;
22import android.content.ComponentName;
23import android.content.ContentResolver;
24import android.content.Context;
25import android.content.Intent;
26import android.content.pm.ApplicationInfo;
27import android.content.pm.IPackageManager;
28import android.content.pm.PackageManager;
29import android.content.pm.ResolveInfo;
30import android.content.pm.ServiceInfo;
31import android.content.res.Configuration;
32import android.content.res.Resources;
33import android.database.ContentObserver;
34import android.hardware.soundtrigger.IRecognitionStatusCallback;
35import android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel;
36import android.hardware.soundtrigger.SoundTrigger.ModuleProperties;
37import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig;
38import android.os.Binder;
39import android.os.Bundle;
40import android.os.Handler;
41import android.os.IBinder;
42import android.os.Parcel;
43import android.os.RemoteException;
44import android.os.UserHandle;
45import android.provider.Settings;
46import android.service.voice.IVoiceInteractionService;
47import android.service.voice.IVoiceInteractionSession;
48import android.service.voice.VoiceInteractionService;
49import android.service.voice.VoiceInteractionServiceInfo;
50import android.speech.RecognitionService;
51import android.text.TextUtils;
52import android.util.Slog;
53
54import com.android.internal.app.IVoiceInteractionManagerService;
55import com.android.internal.app.IVoiceInteractionSessionShowCallback;
56import com.android.internal.app.IVoiceInteractor;
57import com.android.internal.content.PackageMonitor;
58import com.android.internal.os.BackgroundThread;
59import com.android.server.SystemService;
60import com.android.server.UiThread;
61
62import java.io.FileDescriptor;
63import java.io.PrintWriter;
64import java.util.List;
65
66/**
67 * SystemService that publishes an IVoiceInteractionManagerService.
68 */
69public class VoiceInteractionManagerService extends SystemService {
70    static final String TAG = "VoiceInteractionManagerService";
71    static final boolean DEBUG = false;
72
73    final Context mContext;
74    final ContentResolver mResolver;
75    final DatabaseHelper mDbHelper;
76    final SoundTriggerHelper mSoundTriggerHelper;
77
78    public VoiceInteractionManagerService(Context context) {
79        super(context);
80        mContext = context;
81        mResolver = context.getContentResolver();
82        mDbHelper = new DatabaseHelper(context);
83        mSoundTriggerHelper = new SoundTriggerHelper(context);
84        mServiceStub = new VoiceInteractionManagerServiceStub();
85    }
86
87    @Override
88    public void onStart() {
89        publishBinderService(Context.VOICE_INTERACTION_MANAGER_SERVICE, mServiceStub);
90    }
91
92    @Override
93    public void onBootPhase(int phase) {
94        if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
95            mServiceStub.systemRunning(isSafeMode());
96        }
97    }
98
99    @Override
100    public void onStartUser(int userHandle) {
101        mServiceStub.initForUser(userHandle);
102    }
103
104    @Override
105    public void onSwitchUser(int userHandle) {
106        mServiceStub.switchUser(userHandle);
107    }
108
109    // implementation entry point and binder service
110    private final VoiceInteractionManagerServiceStub mServiceStub;
111
112    class VoiceInteractionManagerServiceStub extends IVoiceInteractionManagerService.Stub {
113
114        VoiceInteractionManagerServiceImpl mImpl;
115
116        private boolean mSafeMode;
117        private int mCurUser;
118        private final boolean mEnableService;
119
120        VoiceInteractionManagerServiceStub() {
121            mEnableService = shouldEnableService(mContext.getResources());
122        }
123
124        @Override
125        public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
126                throws RemoteException {
127            try {
128                return super.onTransact(code, data, reply, flags);
129            } catch (RuntimeException e) {
130                // The activity manager only throws security exceptions, so let's
131                // log all others.
132                if (!(e instanceof SecurityException)) {
133                    Slog.wtf(TAG, "VoiceInteractionManagerService Crash", e);
134                }
135                throw e;
136            }
137        }
138
139        public void initForUser(int userHandle) {
140            if (DEBUG) Slog.d(TAG, "**************** initForUser user=" + userHandle);
141            String curInteractorStr = Settings.Secure.getStringForUser(
142                    mContext.getContentResolver(),
143                    Settings.Secure.VOICE_INTERACTION_SERVICE, userHandle);
144            ComponentName curRecognizer = getCurRecognizer(userHandle);
145            VoiceInteractionServiceInfo curInteractorInfo = null;
146            if (DEBUG) Slog.d(TAG, "curInteractorStr=" + curInteractorStr
147                    + " curRecognizer=" + curRecognizer);
148            if (curInteractorStr == null && curRecognizer != null && mEnableService) {
149                // If there is no interactor setting, that means we are upgrading
150                // from an older platform version.  If the current recognizer is not
151                // set or matches the preferred recognizer, then we want to upgrade
152                // the user to have the default voice interaction service enabled.
153                // Note that we don't do this for low-RAM devices, since we aren't
154                // supporting voice interaction services there.
155                curInteractorInfo = findAvailInteractor(userHandle, curRecognizer);
156                if (curInteractorInfo != null) {
157                    // Looks good!  We'll apply this one.  To make it happen, we clear the
158                    // recognizer so that we don't think we have anything set and will
159                    // re-apply the settings.
160                    if (DEBUG) Slog.d(TAG, "No set interactor, found avail: "
161                            + curInteractorInfo.getServiceInfo().name);
162                    curRecognizer = null;
163                }
164            }
165
166            // If we are on a svelte device, make sure an interactor is not currently
167            // enabled; if it is, turn it off.
168            if (!mEnableService && curInteractorStr != null) {
169                if (!TextUtils.isEmpty(curInteractorStr)) {
170                    if (DEBUG) Slog.d(TAG, "Svelte device; disabling interactor");
171                    setCurInteractor(null, userHandle);
172                    curInteractorStr = "";
173                }
174            }
175
176            if (curRecognizer != null) {
177                // If we already have at least a recognizer, then we probably want to
178                // leave things as they are...  unless something has disappeared.
179                IPackageManager pm = AppGlobals.getPackageManager();
180                ServiceInfo interactorInfo = null;
181                ServiceInfo recognizerInfo = null;
182                ComponentName curInteractor = !TextUtils.isEmpty(curInteractorStr)
183                        ? ComponentName.unflattenFromString(curInteractorStr) : null;
184                try {
185                    recognizerInfo = pm.getServiceInfo(curRecognizer, 0, userHandle);
186                    if (curInteractor != null) {
187                        interactorInfo = pm.getServiceInfo(curInteractor, 0, userHandle);
188                    }
189                } catch (RemoteException e) {
190                }
191                // If the apps for the currently set components still exist, then all is okay.
192                if (recognizerInfo != null && (curInteractor == null || interactorInfo != null)) {
193                    if (DEBUG) Slog.d(TAG, "Current interactor/recognizer okay, done!");
194                    return;
195                }
196                if (DEBUG) Slog.d(TAG, "Bad recognizer (" + recognizerInfo + ") or interactor ("
197                        + interactorInfo + ")");
198            }
199
200            // Initializing settings, look for an interactor first (but only on non-svelte).
201            if (curInteractorInfo == null && mEnableService) {
202                curInteractorInfo = findAvailInteractor(userHandle, null);
203            }
204
205            if (curInteractorInfo != null) {
206                // Eventually it will be an error to not specify this.
207                setCurInteractor(new ComponentName(curInteractorInfo.getServiceInfo().packageName,
208                        curInteractorInfo.getServiceInfo().name), userHandle);
209                if (curInteractorInfo.getRecognitionService() != null) {
210                    setCurRecognizer(
211                            new ComponentName(curInteractorInfo.getServiceInfo().packageName,
212                                    curInteractorInfo.getRecognitionService()), userHandle);
213                    return;
214                }
215            }
216
217            // No voice interactor, we'll just set up a simple recognizer.
218            curRecognizer = findAvailRecognizer(null, userHandle);
219            if (curRecognizer != null) {
220                if (curInteractorInfo == null) {
221                    setCurInteractor(null, userHandle);
222                }
223                setCurRecognizer(curRecognizer, userHandle);
224            }
225        }
226
227        private boolean shouldEnableService(Resources res) {
228            // VoiceInteractionService should not be enabled on low ram devices unless it has the config flag.
229            return !ActivityManager.isLowRamDeviceStatic()
230                    || res.getBoolean(com.android.internal.R.bool.config_forceEnableVoiceInteractionService);
231        }
232
233        public void systemRunning(boolean safeMode) {
234            mSafeMode = safeMode;
235
236            mPackageMonitor.register(mContext, BackgroundThread.getHandler().getLooper(),
237                    UserHandle.ALL, true);
238            new SettingsObserver(UiThread.getHandler());
239
240            synchronized (this) {
241                mCurUser = ActivityManager.getCurrentUser();
242                switchImplementationIfNeededLocked(false);
243            }
244        }
245
246        public void switchUser(int userHandle) {
247            synchronized (this) {
248                mCurUser = userHandle;
249                switchImplementationIfNeededLocked(false);
250            }
251        }
252
253        void switchImplementationIfNeededLocked(boolean force) {
254            if (!mSafeMode) {
255                String curService = Settings.Secure.getStringForUser(
256                        mResolver, Settings.Secure.VOICE_INTERACTION_SERVICE, mCurUser);
257                ComponentName serviceComponent = null;
258                if (curService != null && !curService.isEmpty()) {
259                    try {
260                        serviceComponent = ComponentName.unflattenFromString(curService);
261                    } catch (RuntimeException e) {
262                        Slog.wtf(TAG, "Bad voice interaction service name " + curService, e);
263                        serviceComponent = null;
264                    }
265                }
266                if (force || mImpl == null || mImpl.mUser != mCurUser
267                        || !mImpl.mComponent.equals(serviceComponent)) {
268                    mSoundTriggerHelper.stopAllRecognitions();
269                    if (mImpl != null) {
270                        mImpl.shutdownLocked();
271                    }
272                    if (serviceComponent != null) {
273                        mImpl = new VoiceInteractionManagerServiceImpl(mContext,
274                                UiThread.getHandler(), this, mCurUser, serviceComponent);
275                        mImpl.startLocked();
276                    } else {
277                        mImpl = null;
278                    }
279                }
280            }
281        }
282
283        VoiceInteractionServiceInfo findAvailInteractor(int userHandle, ComponentName recognizer) {
284            List<ResolveInfo> available =
285                    mContext.getPackageManager().queryIntentServicesAsUser(
286                            new Intent(VoiceInteractionService.SERVICE_INTERFACE), 0, userHandle);
287            int numAvailable = available.size();
288
289            if (numAvailable == 0) {
290                Slog.w(TAG, "no available voice interaction services found for user " + userHandle);
291                return null;
292            } else {
293                // Find first system package.  We never want to allow third party services to
294                // be automatically selected, because those require approval of the user.
295                VoiceInteractionServiceInfo foundInfo = null;
296                for (int i=0; i<numAvailable; i++) {
297                    ServiceInfo cur = available.get(i).serviceInfo;
298                    if ((cur.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) != 0) {
299                        ComponentName comp = new ComponentName(cur.packageName, cur.name);
300                        try {
301                            VoiceInteractionServiceInfo info = new VoiceInteractionServiceInfo(
302                                    mContext.getPackageManager(), comp, userHandle);
303                            if (info.getParseError() == null) {
304                                if (recognizer == null || info.getServiceInfo().packageName.equals(
305                                        recognizer.getPackageName())) {
306                                    if (foundInfo == null) {
307                                        foundInfo = info;
308                                    } else {
309                                        Slog.w(TAG, "More than one voice interaction service, "
310                                                + "picking first "
311                                                + new ComponentName(
312                                                        foundInfo.getServiceInfo().packageName,
313                                                        foundInfo.getServiceInfo().name)
314                                                + " over "
315                                                + new ComponentName(cur.packageName, cur.name));
316                                    }
317                                }
318                            } else {
319                                Slog.w(TAG, "Bad interaction service " + comp + ": "
320                                        + info.getParseError());
321                            }
322                        } catch (PackageManager.NameNotFoundException e) {
323                            Slog.w(TAG, "Failure looking up interaction service " + comp);
324                        } catch (RemoteException e) {
325                        }
326                    }
327                }
328
329                return foundInfo;
330            }
331        }
332
333        ComponentName getCurInteractor(int userHandle) {
334            String curInteractor = Settings.Secure.getStringForUser(
335                    mContext.getContentResolver(),
336                    Settings.Secure.VOICE_INTERACTION_SERVICE, userHandle);
337            if (TextUtils.isEmpty(curInteractor)) {
338                return null;
339            }
340            if (DEBUG) Slog.d(TAG, "getCurInteractor curInteractor=" + curInteractor
341                    + " user=" + userHandle);
342            return ComponentName.unflattenFromString(curInteractor);
343        }
344
345        void setCurInteractor(ComponentName comp, int userHandle) {
346            Settings.Secure.putStringForUser(mContext.getContentResolver(),
347                    Settings.Secure.VOICE_INTERACTION_SERVICE,
348                    comp != null ? comp.flattenToShortString() : "", userHandle);
349            if (DEBUG) Slog.d(TAG, "setCurInteractor comp=" + comp
350                    + " user=" + userHandle);
351        }
352
353        ComponentName findAvailRecognizer(String prefPackage, int userHandle) {
354            List<ResolveInfo> available =
355                    mContext.getPackageManager().queryIntentServicesAsUser(
356                            new Intent(RecognitionService.SERVICE_INTERFACE), 0, userHandle);
357            int numAvailable = available.size();
358
359            if (numAvailable == 0) {
360                Slog.w(TAG, "no available voice recognition services found for user " + userHandle);
361                return null;
362            } else {
363                if (prefPackage != null) {
364                    for (int i=0; i<numAvailable; i++) {
365                        ServiceInfo serviceInfo = available.get(i).serviceInfo;
366                        if (prefPackage.equals(serviceInfo.packageName)) {
367                            return new ComponentName(serviceInfo.packageName, serviceInfo.name);
368                        }
369                    }
370                }
371                if (numAvailable > 1) {
372                    Slog.w(TAG, "more than one voice recognition service found, picking first");
373                }
374
375                ServiceInfo serviceInfo = available.get(0).serviceInfo;
376                return new ComponentName(serviceInfo.packageName, serviceInfo.name);
377            }
378        }
379
380        ComponentName getCurRecognizer(int userHandle) {
381            String curRecognizer = Settings.Secure.getStringForUser(
382                    mContext.getContentResolver(),
383                    Settings.Secure.VOICE_RECOGNITION_SERVICE, userHandle);
384            if (TextUtils.isEmpty(curRecognizer)) {
385                return null;
386            }
387            if (DEBUG) Slog.d(TAG, "getCurRecognizer curRecognizer=" + curRecognizer
388                    + " user=" + userHandle);
389            return ComponentName.unflattenFromString(curRecognizer);
390        }
391
392        void setCurRecognizer(ComponentName comp, int userHandle) {
393            Settings.Secure.putStringForUser(mContext.getContentResolver(),
394                    Settings.Secure.VOICE_RECOGNITION_SERVICE,
395                    comp != null ? comp.flattenToShortString() : "", userHandle);
396            if (DEBUG) Slog.d(TAG, "setCurRecognizer comp=" + comp
397                    + " user=" + userHandle);
398        }
399
400        @Override
401        public void showSession(IVoiceInteractionService service, Bundle args, int flags) {
402            synchronized (this) {
403                if (mImpl == null || mImpl.mService == null
404                        || service.asBinder() != mImpl.mService.asBinder()) {
405                    throw new SecurityException(
406                            "Caller is not the current voice interaction service");
407                }
408                final long caller = Binder.clearCallingIdentity();
409                try {
410                    mImpl.showSessionLocked(args, flags, null /* showCallback */);
411                } finally {
412                    Binder.restoreCallingIdentity(caller);
413                }
414            }
415        }
416
417        @Override
418        public boolean deliverNewSession(IBinder token, IVoiceInteractionSession session,
419                IVoiceInteractor interactor) {
420            synchronized (this) {
421                if (mImpl == null) {
422                    throw new SecurityException(
423                            "deliverNewSession without running voice interaction service");
424                }
425                final int callingPid = Binder.getCallingPid();
426                final int callingUid = Binder.getCallingUid();
427                final long caller = Binder.clearCallingIdentity();
428                try {
429                    return mImpl.deliverNewSessionLocked(callingPid, callingUid, token, session,
430                            interactor);
431                } finally {
432                    Binder.restoreCallingIdentity(caller);
433                }
434            }
435        }
436
437        @Override
438        public boolean showSessionFromSession(IBinder token, Bundle sessionArgs, int flags) {
439            synchronized (this) {
440                if (mImpl == null) {
441                    Slog.w(TAG, "showSessionFromSession without running voice interaction service");
442                    return false;
443                }
444                final long caller = Binder.clearCallingIdentity();
445                try {
446                    return mImpl.showSessionLocked(sessionArgs, flags, null /* showCallback */);
447                } finally {
448                    Binder.restoreCallingIdentity(caller);
449                }
450            }
451        }
452
453        @Override
454        public boolean hideSessionFromSession(IBinder token) {
455            synchronized (this) {
456                if (mImpl == null) {
457                    Slog.w(TAG, "hideSessionFromSession without running voice interaction service");
458                    return false;
459                }
460                final int callingPid = Binder.getCallingPid();
461                final int callingUid = Binder.getCallingUid();
462                final long caller = Binder.clearCallingIdentity();
463                try {
464                    return mImpl.hideSessionLocked(callingPid, callingUid);
465                } finally {
466                    Binder.restoreCallingIdentity(caller);
467                }
468            }
469        }
470
471        @Override
472        public int startVoiceActivity(IBinder token, Intent intent, String resolvedType) {
473            synchronized (this) {
474                if (mImpl == null) {
475                    Slog.w(TAG, "startVoiceActivity without running voice interaction service");
476                    return ActivityManager.START_CANCELED;
477                }
478                final int callingPid = Binder.getCallingPid();
479                final int callingUid = Binder.getCallingUid();
480                final long caller = Binder.clearCallingIdentity();
481                try {
482                    return mImpl.startVoiceActivityLocked(callingPid, callingUid, token,
483                            intent, resolvedType);
484                } finally {
485                    Binder.restoreCallingIdentity(caller);
486                }
487            }
488        }
489
490        @Override
491        public void setKeepAwake(IBinder token, boolean keepAwake) {
492            synchronized (this) {
493                if (mImpl == null) {
494                    Slog.w(TAG, "setKeepAwake without running voice interaction service");
495                    return;
496                }
497                final int callingPid = Binder.getCallingPid();
498                final int callingUid = Binder.getCallingUid();
499                final long caller = Binder.clearCallingIdentity();
500                try {
501                    mImpl.setKeepAwakeLocked(callingPid, callingUid, token, keepAwake);
502                } finally {
503                    Binder.restoreCallingIdentity(caller);
504                }
505            }
506        }
507
508        @Override
509        public void finish(IBinder token) {
510            synchronized (this) {
511                if (mImpl == null) {
512                    Slog.w(TAG, "finish without running voice interaction service");
513                    return;
514                }
515                final long caller = Binder.clearCallingIdentity();
516                try {
517                    mImpl.finishLocked(token);
518                } finally {
519                    Binder.restoreCallingIdentity(caller);
520                }
521            }
522        }
523
524        //----------------- Model management APIs --------------------------------//
525
526        @Override
527        public KeyphraseSoundModel getKeyphraseSoundModel(int keyphraseId, String bcp47Locale) {
528            enforceCallingPermission(Manifest.permission.MANAGE_VOICE_KEYPHRASES);
529
530            if (bcp47Locale == null) {
531                throw new IllegalArgumentException("Illegal argument(s) in getKeyphraseSoundModel");
532            }
533
534            final int callingUid = UserHandle.getCallingUserId();
535            final long caller = Binder.clearCallingIdentity();
536            try {
537                return mDbHelper.getKeyphraseSoundModel(keyphraseId, callingUid, bcp47Locale);
538            } finally {
539                Binder.restoreCallingIdentity(caller);
540            }
541        }
542
543        @Override
544        public int updateKeyphraseSoundModel(KeyphraseSoundModel model) {
545            enforceCallingPermission(Manifest.permission.MANAGE_VOICE_KEYPHRASES);
546            if (model == null) {
547                throw new IllegalArgumentException("Model must not be null");
548            }
549
550            final long caller = Binder.clearCallingIdentity();
551            try {
552                if (mDbHelper.updateKeyphraseSoundModel(model)) {
553                    synchronized (this) {
554                        // Notify the voice interaction service of a change in sound models.
555                        if (mImpl != null && mImpl.mService != null) {
556                            mImpl.notifySoundModelsChangedLocked();
557                        }
558                    }
559                    return SoundTriggerHelper.STATUS_OK;
560                } else {
561                    return SoundTriggerHelper.STATUS_ERROR;
562                }
563            } finally {
564                Binder.restoreCallingIdentity(caller);
565            }
566        }
567
568        @Override
569        public int deleteKeyphraseSoundModel(int keyphraseId, String bcp47Locale) {
570            enforceCallingPermission(Manifest.permission.MANAGE_VOICE_KEYPHRASES);
571
572            if (bcp47Locale == null) {
573                throw new IllegalArgumentException(
574                        "Illegal argument(s) in deleteKeyphraseSoundModel");
575            }
576
577            final int callingUid = UserHandle.getCallingUserId();
578            final long caller = Binder.clearCallingIdentity();
579            boolean deleted = false;
580            try {
581                deleted = mDbHelper.deleteKeyphraseSoundModel(keyphraseId, callingUid, bcp47Locale);
582                return deleted ? SoundTriggerHelper.STATUS_OK : SoundTriggerHelper.STATUS_ERROR;
583            } finally {
584                if (deleted) {
585                    synchronized (this) {
586                        // Notify the voice interaction service of a change in sound models.
587                        if (mImpl != null && mImpl.mService != null) {
588                            mImpl.notifySoundModelsChangedLocked();
589                        }
590                    }
591                }
592                Binder.restoreCallingIdentity(caller);
593            }
594        }
595
596        //----------------- SoundTrigger APIs --------------------------------//
597        @Override
598        public boolean isEnrolledForKeyphrase(IVoiceInteractionService service, int keyphraseId,
599                String bcp47Locale) {
600            synchronized (this) {
601                if (mImpl == null || mImpl.mService == null
602                        || service.asBinder() != mImpl.mService.asBinder()) {
603                    throw new SecurityException(
604                            "Caller is not the current voice interaction service");
605                }
606            }
607
608            if (bcp47Locale == null) {
609                throw new IllegalArgumentException("Illegal argument(s) in isEnrolledForKeyphrase");
610            }
611
612            final int callingUid = UserHandle.getCallingUserId();
613            final long caller = Binder.clearCallingIdentity();
614            try {
615                KeyphraseSoundModel model =
616                        mDbHelper.getKeyphraseSoundModel(keyphraseId, callingUid, bcp47Locale);
617                return model != null;
618            } finally {
619                Binder.restoreCallingIdentity(caller);
620            }
621        }
622
623        @Override
624        public ModuleProperties getDspModuleProperties(IVoiceInteractionService service) {
625            // Allow the call if this is the current voice interaction service.
626            synchronized (this) {
627                if (mImpl == null || mImpl.mService == null
628                        || service == null || service.asBinder() != mImpl.mService.asBinder()) {
629                    throw new SecurityException(
630                            "Caller is not the current voice interaction service");
631                }
632
633                final long caller = Binder.clearCallingIdentity();
634                try {
635                    return mSoundTriggerHelper.moduleProperties;
636                } finally {
637                    Binder.restoreCallingIdentity(caller);
638                }
639            }
640        }
641
642        @Override
643        public int startRecognition(IVoiceInteractionService service, int keyphraseId,
644                String bcp47Locale, IRecognitionStatusCallback callback,
645                RecognitionConfig recognitionConfig) {
646            // Allow the call if this is the current voice interaction service.
647            synchronized (this) {
648                if (mImpl == null || mImpl.mService == null
649                        || service == null || service.asBinder() != mImpl.mService.asBinder()) {
650                    throw new SecurityException(
651                            "Caller is not the current voice interaction service");
652                }
653
654                if (callback == null || recognitionConfig == null || bcp47Locale == null) {
655                    throw new IllegalArgumentException("Illegal argument(s) in startRecognition");
656                }
657            }
658
659            int callingUid = UserHandle.getCallingUserId();
660            final long caller = Binder.clearCallingIdentity();
661            try {
662                KeyphraseSoundModel soundModel =
663                        mDbHelper.getKeyphraseSoundModel(keyphraseId, callingUid, bcp47Locale);
664                if (soundModel == null
665                        || soundModel.uuid == null
666                        || soundModel.keyphrases == null) {
667                    Slog.w(TAG, "No matching sound model found in startRecognition");
668                    return SoundTriggerHelper.STATUS_ERROR;
669                } else {
670                    return mSoundTriggerHelper.startRecognition(
671                            keyphraseId, soundModel, callback, recognitionConfig);
672                }
673            } finally {
674                Binder.restoreCallingIdentity(caller);
675            }
676        }
677
678        @Override
679        public int stopRecognition(IVoiceInteractionService service, int keyphraseId,
680                IRecognitionStatusCallback callback) {
681            // Allow the call if this is the current voice interaction service.
682            synchronized (this) {
683                if (mImpl == null || mImpl.mService == null
684                        || service == null || service.asBinder() != mImpl.mService.asBinder()) {
685                    throw new SecurityException(
686                            "Caller is not the current voice interaction service");
687                }
688            }
689
690            final long caller = Binder.clearCallingIdentity();
691            try {
692                return mSoundTriggerHelper.stopRecognition(keyphraseId, callback);
693            } finally {
694                Binder.restoreCallingIdentity(caller);
695            }
696        }
697
698        @Override
699        public ComponentName getActiveServiceComponentName() {
700            enforceCallingPermission(Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE);
701            synchronized (this) {
702                return mImpl != null ? mImpl.mComponent : null;
703            }
704        }
705
706        @Override
707        public void showSessionForActiveService(IVoiceInteractionSessionShowCallback showCallback) {
708            enforceCallingPermission(Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE);
709            synchronized (this) {
710                if (mImpl == null) {
711                    Slog.w(TAG, "showSessionForActiveService without running voice interaction"
712                            + "service");
713                    return;
714                }
715                final long caller = Binder.clearCallingIdentity();
716                try {
717                    mImpl.showSessionLocked(new Bundle() /* sessionArgs */,
718                            VoiceInteractionService.START_SOURCE_ASSIST_GESTURE
719                                    | VoiceInteractionService.START_WITH_ASSIST
720                                    | VoiceInteractionService.START_WITH_SCREENSHOT,
721                            showCallback);
722                } finally {
723                    Binder.restoreCallingIdentity(caller);
724                }
725            }
726        }
727
728        @Override
729        public boolean isSessionRunning() {
730            enforceCallingPermission(Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE);
731            synchronized (this) {
732                return mImpl != null && mImpl.mActiveSession != null;
733            }
734        }
735
736        @Override
737        public boolean activeServiceSupportsAssist() {
738            enforceCallingPermission(Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE);
739            synchronized (this) {
740                return mImpl != null && mImpl.mInfo.getSupportsAssist();
741            }
742        }
743
744        @Override
745        public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
746            if (mContext.checkCallingOrSelfPermission(Manifest.permission.DUMP)
747                    != PackageManager.PERMISSION_GRANTED) {
748                pw.println("Permission Denial: can't dump PowerManager from from pid="
749                        + Binder.getCallingPid()
750                        + ", uid=" + Binder.getCallingUid());
751                return;
752            }
753            synchronized (this) {
754                pw.println("VOICE INTERACTION MANAGER (dumpsys voiceinteraction)\n");
755                pw.println("  mEnableService: " + mEnableService);
756                if (mImpl == null) {
757                    pw.println("  (No active implementation)");
758                    return;
759                }
760                mImpl.dumpLocked(fd, pw, args);
761            }
762            mSoundTriggerHelper.dump(fd, pw, args);
763        }
764
765        private void enforceCallingPermission(String permission) {
766            if (mContext.checkCallingPermission(permission) != PackageManager.PERMISSION_GRANTED) {
767                throw new SecurityException("Caller does not hold the permission " + permission);
768            }
769        }
770
771        class SettingsObserver extends ContentObserver {
772            SettingsObserver(Handler handler) {
773                super(handler);
774                ContentResolver resolver = mContext.getContentResolver();
775                resolver.registerContentObserver(Settings.Secure.getUriFor(
776                        Settings.Secure.VOICE_INTERACTION_SERVICE), false, this);
777            }
778
779            @Override public void onChange(boolean selfChange) {
780                synchronized (VoiceInteractionManagerServiceStub.this) {
781                    switchImplementationIfNeededLocked(false);
782                }
783            }
784        }
785
786        PackageMonitor mPackageMonitor = new PackageMonitor() {
787            @Override
788            public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) {
789                return super.onHandleForceStop(intent, packages, uid, doit);
790            }
791
792            @Override
793            public void onHandleUserStop(Intent intent, int userHandle) {
794            }
795
796            @Override
797            public void onSomePackagesChanged() {
798                int userHandle = getChangingUserId();
799                if (DEBUG) Slog.d(TAG, "onSomePackagesChanged user=" + userHandle);
800
801                ComponentName curInteractor = getCurInteractor(userHandle);
802                ComponentName curRecognizer = getCurRecognizer(userHandle);
803                if (curRecognizer == null) {
804                    // Could a new recognizer appear when we don't have one pre-installed?
805                    if (anyPackagesAppearing()) {
806                        curRecognizer = findAvailRecognizer(null, userHandle);
807                        if (curRecognizer != null) {
808                            setCurRecognizer(curRecognizer, userHandle);
809                        }
810                    }
811                    return;
812                }
813
814                if (curInteractor != null) {
815                    int change = isPackageDisappearing(curInteractor.getPackageName());
816                    if (change == PACKAGE_PERMANENT_CHANGE) {
817                        // The currently set interactor is permanently gone; fall back to
818                        // the default config.
819                        setCurInteractor(null, userHandle);
820                        setCurRecognizer(null, userHandle);
821                        initForUser(userHandle);
822                        return;
823                    }
824
825                    change = isPackageAppearing(curInteractor.getPackageName());
826                    if (change != PACKAGE_UNCHANGED) {
827                        // If current interactor is now appearing, for any reason, then
828                        // restart our connection with it.
829                        if (mImpl != null && curInteractor.getPackageName().equals(
830                                mImpl.mComponent.getPackageName())) {
831                            switchImplementationIfNeededLocked(true);
832                        }
833                    }
834                    return;
835                }
836
837                // There is no interactor, so just deal with a simple recognizer.
838                int change = isPackageDisappearing(curRecognizer.getPackageName());
839                if (change == PACKAGE_PERMANENT_CHANGE
840                        || change == PACKAGE_TEMPORARY_CHANGE) {
841                    setCurRecognizer(findAvailRecognizer(null, userHandle), userHandle);
842
843                } else if (isPackageModified(curRecognizer.getPackageName())) {
844                    setCurRecognizer(findAvailRecognizer(curRecognizer.getPackageName(),
845                            userHandle), userHandle);
846                }
847            }
848        };
849    }
850}
851