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