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