[go: nahoru, domu]

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