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