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