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