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