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