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