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