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