VoiceInteractionManagerService.java revision 80cf2210226bc652181abafe606b69ebce63af78
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.SystemProperties;
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.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.i(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 (curInteractorStr == null && curRecognizer != null && mEnableService) {
146                // If there is no interactor setting, that means we are upgrading
147                // from an older platform version.  If the current recognizer is not
148                // set or matches the preferred recognizer, then we want to upgrade
149                // the user to have the default voice interaction service enabled.
150                // Note that we don't do this for low-RAM devices, since we aren't
151                // supporting voice interaction services there.
152                curInteractorInfo = findAvailInteractor(userHandle, curRecognizer.getPackageName());
153                if (curInteractorInfo != null) {
154                    // Looks good!  We'll apply this one.  To make it happen, we clear the
155                    // recognizer so that we don't think we have anything set and will
156                    // re-apply the settings.
157                    curRecognizer = null;
158                }
159            }
160
161            // If forceInteractorPackage exists, try to apply the interactor from this package if
162            // possible and ignore the regular interactor setting.
163            String forceInteractorPackage =
164                    getForceVoiceInteractionServicePackage(mContext.getResources());
165            if (forceInteractorPackage != null) {
166                curInteractorInfo = findAvailInteractor(userHandle, forceInteractorPackage);
167                if (curInteractorInfo != null) {
168                    // We'll apply this one. Clear the recognizer and re-apply the settings.
169                    curRecognizer = null;
170                }
171            }
172
173            // If we are on a svelte device, make sure an interactor is not currently
174            // enabled; if it is, turn it off.
175            if (!mEnableService && curInteractorStr != null) {
176                if (!TextUtils.isEmpty(curInteractorStr)) {
177                    setCurInteractor(null, userHandle);
178                    curInteractorStr = "";
179                }
180            }
181
182            if (curRecognizer != null) {
183                // If we already have at least a recognizer, then we probably want to
184                // leave things as they are...  unless something has disappeared.
185                IPackageManager pm = AppGlobals.getPackageManager();
186                ServiceInfo interactorInfo = null;
187                ServiceInfo recognizerInfo = null;
188                ComponentName curInteractor = !TextUtils.isEmpty(curInteractorStr)
189                        ? ComponentName.unflattenFromString(curInteractorStr) : null;
190                try {
191                    recognizerInfo = pm.getServiceInfo(curRecognizer, 0, userHandle);
192                    if (curInteractor != null) {
193                        interactorInfo = pm.getServiceInfo(curInteractor, 0, userHandle);
194                    }
195                } catch (RemoteException e) {
196                }
197                // If the apps for the currently set components still exist, then all is okay.
198                if (recognizerInfo != null && (curInteractor == null || interactorInfo != null)) {
199                    return;
200                }
201            }
202
203            // Initializing settings, look for an interactor first (but only on non-svelte).
204            if (curInteractorInfo == null && mEnableService) {
205                curInteractorInfo = findAvailInteractor(userHandle, null);
206            }
207
208            if (curInteractorInfo != null) {
209                // Eventually it will be an error to not specify this.
210                setCurInteractor(new ComponentName(curInteractorInfo.getServiceInfo().packageName,
211                        curInteractorInfo.getServiceInfo().name), userHandle);
212                if (curInteractorInfo.getRecognitionService() != null) {
213                    setCurRecognizer(
214                            new ComponentName(curInteractorInfo.getServiceInfo().packageName,
215                                    curInteractorInfo.getRecognitionService()), userHandle);
216                    return;
217                }
218            }
219
220            // No voice interactor, we'll just set up a simple recognizer.
221            curRecognizer = findAvailRecognizer(null, userHandle);
222            if (curRecognizer != null) {
223                if (curInteractorInfo == null) {
224                    setCurInteractor(null, userHandle);
225                }
226                setCurRecognizer(curRecognizer, userHandle);
227            }
228        }
229
230        private boolean shouldEnableService(Resources res) {
231            // VoiceInteractionService should not be enabled on low ram devices unless it has the config flag.
232            return !ActivityManager.isLowRamDeviceStatic() ||
233                    getForceVoiceInteractionServicePackage(res) != null;
234        }
235
236        private String getForceVoiceInteractionServicePackage(Resources res) {
237            String interactorPackage =
238                    res.getString(com.android.internal.R.string.config_forceVoiceInteractionServicePackage);
239            return TextUtils.isEmpty(interactorPackage) ? null : interactorPackage;
240        }
241
242        public void systemRunning(boolean safeMode) {
243            mSafeMode = safeMode;
244
245            mPackageMonitor.register(mContext, BackgroundThread.getHandler().getLooper(),
246                    UserHandle.ALL, true);
247            new SettingsObserver(UiThread.getHandler());
248
249            synchronized (this) {
250                mCurUser = ActivityManager.getCurrentUser();
251                switchImplementationIfNeededLocked(false);
252            }
253        }
254
255        public void switchUser(int userHandle) {
256            synchronized (this) {
257                mCurUser = userHandle;
258                switchImplementationIfNeededLocked(false);
259            }
260        }
261
262        void switchImplementationIfNeededLocked(boolean force) {
263            if (!mSafeMode) {
264                String curService = Settings.Secure.getStringForUser(
265                        mResolver, Settings.Secure.VOICE_INTERACTION_SERVICE, mCurUser);
266                ComponentName serviceComponent = null;
267                if (curService != null && !curService.isEmpty()) {
268                    try {
269                        serviceComponent = ComponentName.unflattenFromString(curService);
270                    } catch (RuntimeException e) {
271                        Slog.wtf(TAG, "Bad voice interaction service name " + curService, e);
272                        serviceComponent = null;
273                    }
274                }
275                if (force || mImpl == null || mImpl.mUser != mCurUser
276                        || !mImpl.mComponent.equals(serviceComponent)) {
277                    mSoundTriggerHelper.stopAllRecognitions();
278                    if (mImpl != null) {
279                        mImpl.shutdownLocked();
280                    }
281                    if (serviceComponent != null) {
282                        mImpl = new VoiceInteractionManagerServiceImpl(mContext,
283                                UiThread.getHandler(), this, mCurUser, serviceComponent);
284                        mImpl.startLocked();
285                    } else {
286                        mImpl = null;
287                    }
288                }
289            }
290        }
291
292        VoiceInteractionServiceInfo findAvailInteractor(int userHandle, String packageName) {
293            List<ResolveInfo> available =
294                    mContext.getPackageManager().queryIntentServicesAsUser(
295                            new Intent(VoiceInteractionService.SERVICE_INTERFACE), 0, userHandle);
296            int numAvailable = available.size();
297
298            if (numAvailable == 0) {
299                Slog.w(TAG, "no available voice interaction services found for user " + userHandle);
300                return null;
301            } else {
302                // Find first system package.  We never want to allow third party services to
303                // be automatically selected, because those require approval of the user.
304                VoiceInteractionServiceInfo foundInfo = null;
305                for (int i=0; i<numAvailable; i++) {
306                    ServiceInfo cur = available.get(i).serviceInfo;
307                    if ((cur.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) != 0) {
308                        ComponentName comp = new ComponentName(cur.packageName, cur.name);
309                        try {
310                            VoiceInteractionServiceInfo info = new VoiceInteractionServiceInfo(
311                                    mContext.getPackageManager(), comp, userHandle);
312                            if (info.getParseError() == null) {
313                                if (packageName == null || info.getServiceInfo().packageName.equals(
314                                        packageName)) {
315                                    if (foundInfo == null) {
316                                        foundInfo = info;
317                                    } else {
318                                        Slog.w(TAG, "More than one voice interaction service, "
319                                                + "picking first "
320                                                + new ComponentName(
321                                                        foundInfo.getServiceInfo().packageName,
322                                                        foundInfo.getServiceInfo().name)
323                                                + " over "
324                                                + new ComponentName(cur.packageName, cur.name));
325                                    }
326                                }
327                            } else {
328                                Slog.w(TAG, "Bad interaction service " + comp + ": "
329                                        + info.getParseError());
330                            }
331                        } catch (PackageManager.NameNotFoundException e) {
332                            Slog.w(TAG, "Failure looking up interaction service " + comp);
333                        } catch (RemoteException e) {
334                        }
335                    }
336                }
337
338                return foundInfo;
339            }
340        }
341
342        ComponentName getCurInteractor(int userHandle) {
343            String curInteractor = Settings.Secure.getStringForUser(
344                    mContext.getContentResolver(),
345                    Settings.Secure.VOICE_INTERACTION_SERVICE, userHandle);
346            if (TextUtils.isEmpty(curInteractor)) {
347                return null;
348            }
349            if (DEBUG) Slog.i(TAG, "getCurInteractor curInteractor=" + curInteractor
350                    + " user=" + userHandle);
351            return ComponentName.unflattenFromString(curInteractor);
352        }
353
354        void setCurInteractor(ComponentName comp, int userHandle) {
355            Settings.Secure.putStringForUser(mContext.getContentResolver(),
356                    Settings.Secure.VOICE_INTERACTION_SERVICE,
357                    comp != null ? comp.flattenToShortString() : "", userHandle);
358            if (DEBUG) Slog.i(TAG, "setCurInteractor comp=" + comp
359                    + " user=" + userHandle);
360        }
361
362        ComponentName findAvailRecognizer(String prefPackage, int userHandle) {
363            List<ResolveInfo> available =
364                    mContext.getPackageManager().queryIntentServicesAsUser(
365                            new Intent(RecognitionService.SERVICE_INTERFACE), 0, userHandle);
366            int numAvailable = available.size();
367
368            if (numAvailable == 0) {
369                Slog.w(TAG, "no available voice recognition services found for user " + userHandle);
370                return null;
371            } else {
372                if (prefPackage != null) {
373                    for (int i=0; i<numAvailable; i++) {
374                        ServiceInfo serviceInfo = available.get(i).serviceInfo;
375                        if (prefPackage.equals(serviceInfo.packageName)) {
376                            return new ComponentName(serviceInfo.packageName, serviceInfo.name);
377                        }
378                    }
379                }
380                if (numAvailable > 1) {
381                    Slog.w(TAG, "more than one voice recognition service found, picking first");
382                }
383
384                ServiceInfo serviceInfo = available.get(0).serviceInfo;
385                return new ComponentName(serviceInfo.packageName, serviceInfo.name);
386            }
387        }
388
389        ComponentName getCurRecognizer(int userHandle) {
390            String curRecognizer = Settings.Secure.getStringForUser(
391                    mContext.getContentResolver(),
392                    Settings.Secure.VOICE_RECOGNITION_SERVICE, userHandle);
393            if (TextUtils.isEmpty(curRecognizer)) {
394                return null;
395            }
396            if (DEBUG) Slog.i(TAG, "getCurRecognizer curRecognizer=" + curRecognizer
397                    + " user=" + userHandle);
398            return ComponentName.unflattenFromString(curRecognizer);
399        }
400
401        void setCurRecognizer(ComponentName comp, int userHandle) {
402            Settings.Secure.putStringForUser(mContext.getContentResolver(),
403                    Settings.Secure.VOICE_RECOGNITION_SERVICE,
404                    comp != null ? comp.flattenToShortString() : "", userHandle);
405            if (DEBUG) Slog.i(TAG, "setCurRecognizer comp=" + comp
406                    + " user=" + userHandle);
407        }
408
409        @Override
410        public void startSession(IVoiceInteractionService service, Bundle args) {
411            synchronized (this) {
412                if (mImpl == null || mImpl.mService == null
413                        || service.asBinder() != mImpl.mService.asBinder()) {
414                    throw new SecurityException(
415                            "Caller is not the current voice interaction service");
416                }
417                final int callingPid = Binder.getCallingPid();
418                final int callingUid = Binder.getCallingUid();
419                final long caller = Binder.clearCallingIdentity();
420                try {
421                    mImpl.startSessionLocked(callingPid, callingUid, args);
422                } finally {
423                    Binder.restoreCallingIdentity(caller);
424                }
425            }
426        }
427
428        @Override
429        public boolean deliverNewSession(IBinder token, IVoiceInteractionSession session,
430                IVoiceInteractor interactor) {
431            synchronized (this) {
432                if (mImpl == null) {
433                    throw new SecurityException(
434                            "deliverNewSession without running voice interaction service");
435                }
436                final int callingPid = Binder.getCallingPid();
437                final int callingUid = Binder.getCallingUid();
438                final long caller = Binder.clearCallingIdentity();
439                try {
440                    return mImpl.deliverNewSessionLocked(callingPid, callingUid, token, session,
441                            interactor);
442                } finally {
443                    Binder.restoreCallingIdentity(caller);
444                }
445            }
446        }
447
448        @Override
449        public int startVoiceActivity(IBinder token, Intent intent, String resolvedType) {
450            synchronized (this) {
451                if (mImpl == null) {
452                    Slog.w(TAG, "startVoiceActivity without running voice interaction service");
453                    return ActivityManager.START_CANCELED;
454                }
455                final int callingPid = Binder.getCallingPid();
456                final int callingUid = Binder.getCallingUid();
457                final long caller = Binder.clearCallingIdentity();
458                if (!SystemProperties.getBoolean("persist.test.voice_interaction", false)) {
459                    throw new SecurityException("Voice interaction not supported");
460                }
461                try {
462                    return mImpl.startVoiceActivityLocked(callingPid, callingUid, token,
463                            intent, resolvedType);
464                } finally {
465                    Binder.restoreCallingIdentity(caller);
466                }
467            }
468        }
469
470        @Override
471        public void finish(IBinder token) {
472            synchronized (this) {
473                if (mImpl == null) {
474                    Slog.w(TAG, "finish without running voice interaction service");
475                    return;
476                }
477                final int callingPid = Binder.getCallingPid();
478                final int callingUid = Binder.getCallingUid();
479                final long caller = Binder.clearCallingIdentity();
480                try {
481                    mImpl.finishLocked(callingPid, callingUid, token);
482                } finally {
483                    Binder.restoreCallingIdentity(caller);
484                }
485            }
486        }
487
488        //----------------- Model management APIs --------------------------------//
489
490        @Override
491        public KeyphraseSoundModel getKeyphraseSoundModel(int keyphraseId, String bcp47Locale) {
492            synchronized (this) {
493                if (mContext.checkCallingPermission(Manifest.permission.MANAGE_VOICE_KEYPHRASES)
494                        != PackageManager.PERMISSION_GRANTED) {
495                    throw new SecurityException("Caller does not hold the permission "
496                            + Manifest.permission.MANAGE_VOICE_KEYPHRASES);
497                }
498            }
499
500            if (bcp47Locale == null) {
501                throw new IllegalArgumentException("Illegal argument(s) in getKeyphraseSoundModel");
502            }
503
504            final int callingUid = UserHandle.getCallingUserId();
505            final long caller = Binder.clearCallingIdentity();
506            try {
507                return mDbHelper.getKeyphraseSoundModel(keyphraseId, callingUid, bcp47Locale);
508            } finally {
509                Binder.restoreCallingIdentity(caller);
510            }
511        }
512
513        @Override
514        public int updateKeyphraseSoundModel(KeyphraseSoundModel model) {
515            synchronized (this) {
516                if (mContext.checkCallingPermission(Manifest.permission.MANAGE_VOICE_KEYPHRASES)
517                        != PackageManager.PERMISSION_GRANTED) {
518                    throw new SecurityException("Caller does not hold the permission "
519                            + Manifest.permission.MANAGE_VOICE_KEYPHRASES);
520                }
521                if (model == null) {
522                    throw new IllegalArgumentException("Model must not be null");
523                }
524            }
525
526            final long caller = Binder.clearCallingIdentity();
527            try {
528                if (mDbHelper.updateKeyphraseSoundModel(model)) {
529                    synchronized (this) {
530                        // Notify the voice interaction service of a change in sound models.
531                        if (mImpl != null && mImpl.mService != null) {
532                            mImpl.notifySoundModelsChangedLocked();
533                        }
534                    }
535                    return SoundTriggerHelper.STATUS_OK;
536                } else {
537                    return SoundTriggerHelper.STATUS_ERROR;
538                }
539            } finally {
540                Binder.restoreCallingIdentity(caller);
541            }
542        }
543
544        @Override
545        public int deleteKeyphraseSoundModel(int keyphraseId, String bcp47Locale) {
546            synchronized (this) {
547                if (mContext.checkCallingPermission(Manifest.permission.MANAGE_VOICE_KEYPHRASES)
548                        != PackageManager.PERMISSION_GRANTED) {
549                    throw new SecurityException("Caller does not hold the permission "
550                            + Manifest.permission.MANAGE_VOICE_KEYPHRASES);
551                }
552            }
553
554            if (bcp47Locale == null) {
555                throw new IllegalArgumentException(
556                        "Illegal argument(s) in deleteKeyphraseSoundModel");
557            }
558
559            final int callingUid = UserHandle.getCallingUserId();
560            final long caller = Binder.clearCallingIdentity();
561            boolean deleted = false;
562            try {
563                deleted = mDbHelper.deleteKeyphraseSoundModel(keyphraseId, callingUid, bcp47Locale);
564                return deleted ? SoundTriggerHelper.STATUS_OK : SoundTriggerHelper.STATUS_ERROR;
565            } finally {
566                if (deleted) {
567                    synchronized (this) {
568                        // Notify the voice interaction service of a change in sound models.
569                        if (mImpl != null && mImpl.mService != null) {
570                            mImpl.notifySoundModelsChangedLocked();
571                        }
572                    }
573                }
574                Binder.restoreCallingIdentity(caller);
575            }
576        }
577
578        //----------------- SoundTrigger APIs --------------------------------//
579        @Override
580        public boolean isEnrolledForKeyphrase(IVoiceInteractionService service, int keyphraseId,
581                String bcp47Locale) {
582            synchronized (this) {
583                if (mImpl == null || mImpl.mService == null
584                        || service.asBinder() != mImpl.mService.asBinder()) {
585                    throw new SecurityException(
586                            "Caller is not the current voice interaction service");
587                }
588            }
589
590            if (bcp47Locale == null) {
591                throw new IllegalArgumentException("Illegal argument(s) in isEnrolledForKeyphrase");
592            }
593
594            final int callingUid = UserHandle.getCallingUserId();
595            final long caller = Binder.clearCallingIdentity();
596            try {
597                KeyphraseSoundModel model =
598                        mDbHelper.getKeyphraseSoundModel(keyphraseId, callingUid, bcp47Locale);
599                return model != null;
600            } finally {
601                Binder.restoreCallingIdentity(caller);
602            }
603        }
604
605        @Override
606        public ModuleProperties getDspModuleProperties(IVoiceInteractionService service) {
607            // Allow the call if this is the current voice interaction service.
608            synchronized (this) {
609                if (mImpl == null || mImpl.mService == null
610                        || service == null || service.asBinder() != mImpl.mService.asBinder()) {
611                    throw new SecurityException(
612                            "Caller is not the current voice interaction service");
613                }
614
615                final long caller = Binder.clearCallingIdentity();
616                try {
617                    return mSoundTriggerHelper.moduleProperties;
618                } finally {
619                    Binder.restoreCallingIdentity(caller);
620                }
621            }
622        }
623
624        @Override
625        public int startRecognition(IVoiceInteractionService service, int keyphraseId,
626                String bcp47Locale, IRecognitionStatusCallback callback,
627                RecognitionConfig recognitionConfig) {
628            // Allow the call if this is the current voice interaction service.
629            synchronized (this) {
630                if (mImpl == null || mImpl.mService == null
631                        || service == null || service.asBinder() != mImpl.mService.asBinder()) {
632                    throw new SecurityException(
633                            "Caller is not the current voice interaction service");
634                }
635
636                if (callback == null || recognitionConfig == null || bcp47Locale == null) {
637                    throw new IllegalArgumentException("Illegal argument(s) in startRecognition");
638                }
639            }
640
641            int callingUid = UserHandle.getCallingUserId();
642            final long caller = Binder.clearCallingIdentity();
643            try {
644                KeyphraseSoundModel soundModel =
645                        mDbHelper.getKeyphraseSoundModel(keyphraseId, callingUid, bcp47Locale);
646                if (soundModel == null
647                        || soundModel.uuid == null
648                        || soundModel.keyphrases == null) {
649                    Slog.w(TAG, "No matching sound model found in startRecognition");
650                    return SoundTriggerHelper.STATUS_ERROR;
651                } else {
652                    return mSoundTriggerHelper.startRecognition(
653                            keyphraseId, soundModel, callback, recognitionConfig);
654                }
655            } finally {
656                Binder.restoreCallingIdentity(caller);
657            }
658        }
659
660        @Override
661        public int stopRecognition(IVoiceInteractionService service, int keyphraseId,
662                IRecognitionStatusCallback callback) {
663            // Allow the call if this is the current voice interaction service.
664            synchronized (this) {
665                if (mImpl == null || mImpl.mService == null
666                        || service == null || service.asBinder() != mImpl.mService.asBinder()) {
667                    throw new SecurityException(
668                            "Caller is not the current voice interaction service");
669                }
670            }
671
672            final long caller = Binder.clearCallingIdentity();
673            try {
674                return mSoundTriggerHelper.stopRecognition(keyphraseId, callback);
675            } finally {
676                Binder.restoreCallingIdentity(caller);
677            }
678        }
679
680        @Override
681        public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
682            if (mContext.checkCallingOrSelfPermission(Manifest.permission.DUMP)
683                    != PackageManager.PERMISSION_GRANTED) {
684                pw.println("Permission Denial: can't dump PowerManager from from pid="
685                        + Binder.getCallingPid()
686                        + ", uid=" + Binder.getCallingUid());
687                return;
688            }
689            synchronized (this) {
690                pw.println("VOICE INTERACTION MANAGER (dumpsys voiceinteraction)\n");
691                pw.println("  mEnableService: " + mEnableService);
692                if (mImpl == null) {
693                    pw.println("  (No active implementation)");
694                    return;
695                }
696                mImpl.dumpLocked(fd, pw, args);
697            }
698            mSoundTriggerHelper.dump(fd, pw, args);
699        }
700
701        class SettingsObserver extends ContentObserver {
702            SettingsObserver(Handler handler) {
703                super(handler);
704                ContentResolver resolver = mContext.getContentResolver();
705                resolver.registerContentObserver(Settings.Secure.getUriFor(
706                        Settings.Secure.VOICE_INTERACTION_SERVICE), false, this);
707            }
708
709            @Override public void onChange(boolean selfChange) {
710                synchronized (VoiceInteractionManagerServiceStub.this) {
711                    switchImplementationIfNeededLocked(false);
712                }
713            }
714        }
715
716        PackageMonitor mPackageMonitor = new PackageMonitor() {
717            @Override
718            public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) {
719                return super.onHandleForceStop(intent, packages, uid, doit);
720            }
721
722            @Override
723            public void onHandleUserStop(Intent intent, int userHandle) {
724            }
725
726            @Override
727            public void onSomePackagesChanged() {
728                int userHandle = getChangingUserId();
729                if (DEBUG) Slog.i(TAG, "onSomePackagesChanged user=" + userHandle);
730
731                ComponentName curInteractor = getCurInteractor(userHandle);
732                ComponentName curRecognizer = getCurRecognizer(userHandle);
733                if (curRecognizer == null) {
734                    // Could a new recognizer appear when we don't have one pre-installed?
735                    if (anyPackagesAppearing()) {
736                        curRecognizer = findAvailRecognizer(null, userHandle);
737                        if (curRecognizer != null) {
738                            setCurRecognizer(curRecognizer, userHandle);
739                        }
740                    }
741                    return;
742                }
743
744                if (curInteractor != null) {
745                    int change = isPackageDisappearing(curInteractor.getPackageName());
746                    if (change == PACKAGE_PERMANENT_CHANGE) {
747                        // The currently set interactor is permanently gone; fall back to
748                        // the default config.
749                        setCurInteractor(null, userHandle);
750                        setCurRecognizer(null, userHandle);
751                        initForUser(userHandle);
752                        return;
753                    }
754
755                    change = isPackageAppearing(curInteractor.getPackageName());
756                    if (change != PACKAGE_UNCHANGED) {
757                        // If current interactor is now appearing, for any reason, then
758                        // restart our connection with it.
759                        if (mImpl != null && curInteractor.getPackageName().equals(
760                                mImpl.mComponent.getPackageName())) {
761                            switchImplementationIfNeededLocked(true);
762                        }
763                    }
764                    return;
765                }
766
767                // There is no interactor, so just deal with a simple recognizer.
768                int change = isPackageDisappearing(curRecognizer.getPackageName());
769                if (change == PACKAGE_PERMANENT_CHANGE
770                        || change == PACKAGE_TEMPORARY_CHANGE) {
771                    setCurRecognizer(findAvailRecognizer(null, userHandle), userHandle);
772
773                } else if (isPackageModified(curRecognizer.getPackageName())) {
774                    setCurRecognizer(findAvailRecognizer(curRecognizer.getPackageName(),
775                            userHandle), userHandle);
776                }
777            }
778        };
779    }
780}
781