VoiceInteractionManagerService.java revision adc1cf46045ae756d3a9ccbccf6b0f894e4c1edd
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        @Override
437        public void showSession(IVoiceInteractionService service, Bundle args, int flags) {
438            synchronized (this) {
439                if (mImpl == null || mImpl.mService == null
440                        || service.asBinder() != mImpl.mService.asBinder()) {
441                    throw new SecurityException(
442                            "Caller is not the current voice interaction service");
443                }
444                final long caller = Binder.clearCallingIdentity();
445                try {
446                    mImpl.showSessionLocked(args, flags, null /* showCallback */);
447                } finally {
448                    Binder.restoreCallingIdentity(caller);
449                }
450            }
451        }
452
453        @Override
454        public boolean deliverNewSession(IBinder token, IVoiceInteractionSession session,
455                IVoiceInteractor interactor) {
456            synchronized (this) {
457                if (mImpl == null) {
458                    throw new SecurityException(
459                            "deliverNewSession without running voice interaction service");
460                }
461                final int callingPid = Binder.getCallingPid();
462                final int callingUid = Binder.getCallingUid();
463                final long caller = Binder.clearCallingIdentity();
464                try {
465                    return mImpl.deliverNewSessionLocked(callingPid, callingUid, token, session,
466                            interactor);
467                } finally {
468                    Binder.restoreCallingIdentity(caller);
469                }
470            }
471        }
472
473        @Override
474        public boolean showSessionFromSession(IBinder token, Bundle sessionArgs, int flags) {
475            synchronized (this) {
476                if (mImpl == null) {
477                    Slog.w(TAG, "showSessionFromSession without running voice interaction service");
478                    return false;
479                }
480                final long caller = Binder.clearCallingIdentity();
481                try {
482                    return mImpl.showSessionLocked(sessionArgs, flags, null /* showCallback */);
483                } finally {
484                    Binder.restoreCallingIdentity(caller);
485                }
486            }
487        }
488
489        @Override
490        public boolean hideSessionFromSession(IBinder token) {
491            synchronized (this) {
492                if (mImpl == null) {
493                    Slog.w(TAG, "hideSessionFromSession without running voice interaction service");
494                    return false;
495                }
496                final long caller = Binder.clearCallingIdentity();
497                try {
498                    return mImpl.hideSessionLocked();
499                } finally {
500                    Binder.restoreCallingIdentity(caller);
501                }
502            }
503        }
504
505        @Override
506        public int startVoiceActivity(IBinder token, Intent intent, String resolvedType) {
507            synchronized (this) {
508                if (mImpl == null) {
509                    Slog.w(TAG, "startVoiceActivity without running voice interaction service");
510                    return ActivityManager.START_CANCELED;
511                }
512                final int callingPid = Binder.getCallingPid();
513                final int callingUid = Binder.getCallingUid();
514                final long caller = Binder.clearCallingIdentity();
515                try {
516                    return mImpl.startVoiceActivityLocked(callingPid, callingUid, token,
517                            intent, resolvedType);
518                } finally {
519                    Binder.restoreCallingIdentity(caller);
520                }
521            }
522        }
523
524        @Override
525        public void setKeepAwake(IBinder token, boolean keepAwake) {
526            synchronized (this) {
527                if (mImpl == null) {
528                    Slog.w(TAG, "setKeepAwake without running voice interaction service");
529                    return;
530                }
531                final int callingPid = Binder.getCallingPid();
532                final int callingUid = Binder.getCallingUid();
533                final long caller = Binder.clearCallingIdentity();
534                try {
535                    mImpl.setKeepAwakeLocked(callingPid, callingUid, token, keepAwake);
536                } finally {
537                    Binder.restoreCallingIdentity(caller);
538                }
539            }
540        }
541
542        @Override
543        public void finish(IBinder token) {
544            synchronized (this) {
545                if (mImpl == null) {
546                    Slog.w(TAG, "finish without running voice interaction service");
547                    return;
548                }
549                final long caller = Binder.clearCallingIdentity();
550                try {
551                    mImpl.finishLocked(token);
552                } finally {
553                    Binder.restoreCallingIdentity(caller);
554                }
555            }
556        }
557
558        //----------------- Model management APIs --------------------------------//
559
560        @Override
561        public KeyphraseSoundModel getKeyphraseSoundModel(int keyphraseId, String bcp47Locale) {
562            enforceCallingPermission(Manifest.permission.MANAGE_VOICE_KEYPHRASES);
563
564            if (bcp47Locale == null) {
565                throw new IllegalArgumentException("Illegal argument(s) in getKeyphraseSoundModel");
566            }
567
568            final int callingUid = UserHandle.getCallingUserId();
569            final long caller = Binder.clearCallingIdentity();
570            try {
571                return mDbHelper.getKeyphraseSoundModel(keyphraseId, callingUid, bcp47Locale);
572            } finally {
573                Binder.restoreCallingIdentity(caller);
574            }
575        }
576
577        @Override
578        public int updateKeyphraseSoundModel(KeyphraseSoundModel model) {
579            enforceCallingPermission(Manifest.permission.MANAGE_VOICE_KEYPHRASES);
580            if (model == null) {
581                throw new IllegalArgumentException("Model must not be null");
582            }
583
584            final long caller = Binder.clearCallingIdentity();
585            try {
586                if (mDbHelper.updateKeyphraseSoundModel(model)) {
587                    synchronized (this) {
588                        // Notify the voice interaction service of a change in sound models.
589                        if (mImpl != null && mImpl.mService != null) {
590                            mImpl.notifySoundModelsChangedLocked();
591                        }
592                    }
593                    return SoundTriggerHelper.STATUS_OK;
594                } else {
595                    return SoundTriggerHelper.STATUS_ERROR;
596                }
597            } finally {
598                Binder.restoreCallingIdentity(caller);
599            }
600        }
601
602        @Override
603        public int deleteKeyphraseSoundModel(int keyphraseId, String bcp47Locale) {
604            enforceCallingPermission(Manifest.permission.MANAGE_VOICE_KEYPHRASES);
605
606            if (bcp47Locale == null) {
607                throw new IllegalArgumentException(
608                        "Illegal argument(s) in deleteKeyphraseSoundModel");
609            }
610
611            final int callingUid = UserHandle.getCallingUserId();
612            final long caller = Binder.clearCallingIdentity();
613            boolean deleted = false;
614            try {
615                deleted = mDbHelper.deleteKeyphraseSoundModel(keyphraseId, callingUid, bcp47Locale);
616                return deleted ? SoundTriggerHelper.STATUS_OK : SoundTriggerHelper.STATUS_ERROR;
617            } finally {
618                if (deleted) {
619                    synchronized (this) {
620                        // Notify the voice interaction service of a change in sound models.
621                        if (mImpl != null && mImpl.mService != null) {
622                            mImpl.notifySoundModelsChangedLocked();
623                        }
624                    }
625                }
626                Binder.restoreCallingIdentity(caller);
627            }
628        }
629
630        //----------------- SoundTrigger APIs --------------------------------//
631        @Override
632        public boolean isEnrolledForKeyphrase(IVoiceInteractionService service, int keyphraseId,
633                String bcp47Locale) {
634            synchronized (this) {
635                if (mImpl == null || mImpl.mService == null
636                        || service.asBinder() != mImpl.mService.asBinder()) {
637                    throw new SecurityException(
638                            "Caller is not the current voice interaction service");
639                }
640            }
641
642            if (bcp47Locale == null) {
643                throw new IllegalArgumentException("Illegal argument(s) in isEnrolledForKeyphrase");
644            }
645
646            final int callingUid = UserHandle.getCallingUserId();
647            final long caller = Binder.clearCallingIdentity();
648            try {
649                KeyphraseSoundModel model =
650                        mDbHelper.getKeyphraseSoundModel(keyphraseId, callingUid, bcp47Locale);
651                return model != null;
652            } finally {
653                Binder.restoreCallingIdentity(caller);
654            }
655        }
656
657        @Override
658        public ModuleProperties getDspModuleProperties(IVoiceInteractionService service) {
659            // Allow the call if this is the current voice interaction service.
660            synchronized (this) {
661                if (mImpl == null || mImpl.mService == null
662                        || service == null || service.asBinder() != mImpl.mService.asBinder()) {
663                    throw new SecurityException(
664                            "Caller is not the current voice interaction service");
665                }
666
667                final long caller = Binder.clearCallingIdentity();
668                try {
669                    return mSoundTriggerHelper.moduleProperties;
670                } finally {
671                    Binder.restoreCallingIdentity(caller);
672                }
673            }
674        }
675
676        @Override
677        public int startRecognition(IVoiceInteractionService service, int keyphraseId,
678                String bcp47Locale, IRecognitionStatusCallback callback,
679                RecognitionConfig recognitionConfig) {
680            // Allow the call if this is the current voice interaction service.
681            synchronized (this) {
682                if (mImpl == null || mImpl.mService == null
683                        || service == null || service.asBinder() != mImpl.mService.asBinder()) {
684                    throw new SecurityException(
685                            "Caller is not the current voice interaction service");
686                }
687
688                if (callback == null || recognitionConfig == null || bcp47Locale == null) {
689                    throw new IllegalArgumentException("Illegal argument(s) in startRecognition");
690                }
691            }
692
693            int callingUid = UserHandle.getCallingUserId();
694            final long caller = Binder.clearCallingIdentity();
695            try {
696                KeyphraseSoundModel soundModel =
697                        mDbHelper.getKeyphraseSoundModel(keyphraseId, callingUid, bcp47Locale);
698                if (soundModel == null
699                        || soundModel.uuid == null
700                        || soundModel.keyphrases == null) {
701                    Slog.w(TAG, "No matching sound model found in startRecognition");
702                    return SoundTriggerHelper.STATUS_ERROR;
703                } else {
704                    return mSoundTriggerHelper.startRecognition(
705                            keyphraseId, soundModel, callback, recognitionConfig);
706                }
707            } finally {
708                Binder.restoreCallingIdentity(caller);
709            }
710        }
711
712        @Override
713        public int stopRecognition(IVoiceInteractionService service, int keyphraseId,
714                IRecognitionStatusCallback callback) {
715            // Allow the call if this is the current voice interaction service.
716            synchronized (this) {
717                if (mImpl == null || mImpl.mService == null
718                        || service == null || service.asBinder() != mImpl.mService.asBinder()) {
719                    throw new SecurityException(
720                            "Caller is not the current voice interaction service");
721                }
722            }
723
724            final long caller = Binder.clearCallingIdentity();
725            try {
726                return mSoundTriggerHelper.stopRecognition(keyphraseId, callback);
727            } finally {
728                Binder.restoreCallingIdentity(caller);
729            }
730        }
731
732        @Override
733        public ComponentName getActiveServiceComponentName() {
734            enforceCallingPermission(Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE);
735            synchronized (this) {
736                return mImpl != null ? mImpl.mComponent : null;
737            }
738        }
739
740        @Override
741        public void showSessionForActiveService(IVoiceInteractionSessionShowCallback showCallback) {
742            enforceCallingPermission(Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE);
743            synchronized (this) {
744                if (mImpl == null) {
745                    Slog.w(TAG, "showSessionForActiveService without running voice interaction"
746                            + "service");
747                    return;
748                }
749                final long caller = Binder.clearCallingIdentity();
750                try {
751                    mImpl.showSessionLocked(new Bundle() /* sessionArgs */,
752                            VoiceInteractionSession.SHOW_SOURCE_ASSIST_GESTURE
753                                    | VoiceInteractionSession.SHOW_WITH_ASSIST
754                                    | VoiceInteractionSession.SHOW_WITH_SCREENSHOT,
755                            showCallback);
756                } finally {
757                    Binder.restoreCallingIdentity(caller);
758                }
759            }
760        }
761
762        @Override
763        public void hideCurrentSession() throws RemoteException {
764            enforceCallingPermission(Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE);
765            synchronized (this) {
766                if (mImpl == null) {
767                    return;
768                }
769                final long caller = Binder.clearCallingIdentity();
770                try {
771                    if (mImpl.mActiveSession != null && mImpl.mActiveSession.mSession != null) {
772                        try {
773                            mImpl.mActiveSession.mSession.closeSystemDialogs();
774                        } catch (RemoteException e) {
775                            Log.w(TAG, "Failed to call closeSystemDialogs", e);
776                        }
777                    }
778                } finally {
779                    Binder.restoreCallingIdentity(caller);
780                }
781            }
782        }
783
784        @Override
785        public void launchVoiceAssistFromKeyguard() {
786            enforceCallingPermission(Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE);
787            synchronized (this) {
788                if (mImpl == null) {
789                    Slog.w(TAG, "launchVoiceAssistFromKeyguard without running voice interaction"
790                            + "service");
791                    return;
792                }
793                final long caller = Binder.clearCallingIdentity();
794                try {
795                    mImpl.launchVoiceAssistFromKeyguard();
796                } finally {
797                    Binder.restoreCallingIdentity(caller);
798                }
799            }
800        }
801
802        @Override
803        public boolean isSessionRunning() {
804            enforceCallingPermission(Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE);
805            synchronized (this) {
806                return mImpl != null && mImpl.mActiveSession != null;
807            }
808        }
809
810        @Override
811        public boolean activeServiceSupportsAssist() {
812            enforceCallingPermission(Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE);
813            synchronized (this) {
814                return mImpl != null && mImpl.mInfo != null && mImpl.mInfo.getSupportsAssist();
815            }
816        }
817
818        @Override
819        public boolean activeServiceSupportsLaunchFromKeyguard() throws RemoteException {
820            enforceCallingPermission(Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE);
821            synchronized (this) {
822                return mImpl != null && mImpl.mInfo != null
823                        && mImpl.mInfo.getSupportsLaunchFromKeyguard();
824            }
825        }
826
827        @Override
828        public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
829            if (mContext.checkCallingOrSelfPermission(Manifest.permission.DUMP)
830                    != PackageManager.PERMISSION_GRANTED) {
831                pw.println("Permission Denial: can't dump PowerManager from from pid="
832                        + Binder.getCallingPid()
833                        + ", uid=" + Binder.getCallingUid());
834                return;
835            }
836            synchronized (this) {
837                pw.println("VOICE INTERACTION MANAGER (dumpsys voiceinteraction)\n");
838                pw.println("  mEnableService: " + mEnableService);
839                if (mImpl == null) {
840                    pw.println("  (No active implementation)");
841                    return;
842                }
843                mImpl.dumpLocked(fd, pw, args);
844            }
845            mSoundTriggerHelper.dump(fd, pw, args);
846        }
847
848        private void enforceCallingPermission(String permission) {
849            if (mContext.checkCallingPermission(permission) != PackageManager.PERMISSION_GRANTED) {
850                throw new SecurityException("Caller does not hold the permission " + permission);
851            }
852        }
853
854        class SettingsObserver extends ContentObserver {
855            SettingsObserver(Handler handler) {
856                super(handler);
857                ContentResolver resolver = mContext.getContentResolver();
858                resolver.registerContentObserver(Settings.Secure.getUriFor(
859                        Settings.Secure.VOICE_INTERACTION_SERVICE), false, this,
860                        UserHandle.USER_ALL);
861            }
862
863            @Override public void onChange(boolean selfChange) {
864                synchronized (VoiceInteractionManagerServiceStub.this) {
865                    switchImplementationIfNeededLocked(false);
866                }
867            }
868        }
869
870        PackageMonitor mPackageMonitor = new PackageMonitor() {
871            @Override
872            public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) {
873                if (DEBUG) Slog.d(TAG, "onHandleForceStop uid=" + uid + " doit=" + doit);
874
875                int userHandle = UserHandle.getUserId(uid);
876                ComponentName curInteractor = getCurInteractor(userHandle);
877                ComponentName curRecognizer = getCurRecognizer(userHandle);
878                boolean hit = false;
879                for (String pkg : packages) {
880                    if (curInteractor != null && pkg.equals(curInteractor.getPackageName())) {
881                        hit = true;
882                        break;
883                    } else if (curRecognizer != null
884                            && pkg.equals(curRecognizer.getPackageName())) {
885                        hit = true;
886                        break;
887                    }
888                }
889                if (hit && doit) {
890                    // The user is force stopping our current interactor/recognizer.
891                    // Clear the current settings and restore default state.
892                    synchronized (VoiceInteractionManagerService.this) {
893                        mSoundTriggerHelper.stopAllRecognitions();
894                        if (mImpl != null) {
895                            mImpl.shutdownLocked();
896                            mImpl = null;
897                        }
898                        setCurInteractor(null, userHandle);
899                        setCurRecognizer(null, userHandle);
900                        initForUser(userHandle);
901                        switchImplementationIfNeededLocked(true);
902                    }
903                }
904                return hit;
905            }
906
907            @Override
908            public void onHandleUserStop(Intent intent, int userHandle) {
909            }
910
911            @Override
912            public void onSomePackagesChanged() {
913                int userHandle = getChangingUserId();
914                if (DEBUG) Slog.d(TAG, "onSomePackagesChanged user=" + userHandle);
915
916                synchronized (VoiceInteractionManagerService.this) {
917                    ComponentName curInteractor = getCurInteractor(userHandle);
918                    ComponentName curRecognizer = getCurRecognizer(userHandle);
919                    if (curRecognizer == null) {
920                        // Could a new recognizer appear when we don't have one pre-installed?
921                        if (anyPackagesAppearing()) {
922                            curRecognizer = findAvailRecognizer(null, userHandle);
923                            if (curRecognizer != null) {
924                                setCurRecognizer(curRecognizer, userHandle);
925                            }
926                        }
927                        return;
928                    }
929
930                    if (curInteractor != null) {
931                        int change = isPackageDisappearing(curInteractor.getPackageName());
932                        if (change == PACKAGE_PERMANENT_CHANGE) {
933                            // The currently set interactor is permanently gone; fall back to
934                            // the default config.
935                            setCurInteractor(null, userHandle);
936                            setCurRecognizer(null, userHandle);
937                            initForUser(userHandle);
938                            return;
939                        }
940
941                        change = isPackageAppearing(curInteractor.getPackageName());
942                        if (change != PACKAGE_UNCHANGED) {
943                            // If current interactor is now appearing, for any reason, then
944                            // restart our connection with it.
945                            if (mImpl != null && curInteractor.getPackageName().equals(
946                                    mImpl.mComponent.getPackageName())) {
947                                switchImplementationIfNeededLocked(true);
948                            }
949                        }
950                        return;
951                    }
952
953                    // There is no interactor, so just deal with a simple recognizer.
954                    int change = isPackageDisappearing(curRecognizer.getPackageName());
955                    if (change == PACKAGE_PERMANENT_CHANGE
956                            || change == PACKAGE_TEMPORARY_CHANGE) {
957                        setCurRecognizer(findAvailRecognizer(null, userHandle), userHandle);
958
959                    } else if (isPackageModified(curRecognizer.getPackageName())) {
960                        setCurRecognizer(findAvailRecognizer(curRecognizer.getPackageName(),
961                                userHandle), userHandle);
962                    }
963                }
964            }
965        };
966    }
967}
968