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