| /* |
| * Copyright (C) 2014 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.server.voiceinteraction; |
| |
| import android.Manifest; |
| import android.app.ActivityManager; |
| import android.app.ActivityManagerInternal; |
| import android.app.AppGlobals; |
| import android.content.ComponentName; |
| import android.content.ContentResolver; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.pm.ApplicationInfo; |
| import android.content.pm.IPackageManager; |
| import android.content.pm.PackageManager; |
| import android.content.pm.PackageManagerInternal; |
| import android.content.pm.ResolveInfo; |
| import android.content.pm.ServiceInfo; |
| import android.content.pm.ShortcutServiceInternal; |
| import android.content.res.Resources; |
| import android.database.ContentObserver; |
| import android.hardware.soundtrigger.IRecognitionStatusCallback; |
| import android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel; |
| import android.hardware.soundtrigger.SoundTrigger.ModuleProperties; |
| import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig; |
| import android.os.Binder; |
| import android.os.Bundle; |
| import android.os.Handler; |
| import android.os.IBinder; |
| import android.os.Parcel; |
| import android.os.RemoteCallbackList; |
| import android.os.RemoteException; |
| import android.os.UserHandle; |
| import android.os.UserManager; |
| import android.provider.Settings; |
| import android.service.voice.IVoiceInteractionService; |
| import android.service.voice.IVoiceInteractionSession; |
| import android.service.voice.VoiceInteractionManagerInternal; |
| import android.service.voice.VoiceInteractionService; |
| import android.service.voice.VoiceInteractionServiceInfo; |
| import android.service.voice.VoiceInteractionSession; |
| import android.speech.RecognitionService; |
| import android.text.TextUtils; |
| import android.util.ArraySet; |
| import android.util.Log; |
| import android.util.Slog; |
| |
| import com.android.internal.app.IVoiceInteractionSessionListener; |
| import com.android.internal.app.IVoiceInteractionManagerService; |
| import com.android.internal.app.IVoiceInteractionSessionShowCallback; |
| import com.android.internal.app.IVoiceInteractor; |
| import com.android.internal.content.PackageMonitor; |
| import com.android.internal.os.BackgroundThread; |
| import com.android.internal.util.DumpUtils; |
| import com.android.internal.util.Preconditions; |
| import com.android.server.LocalServices; |
| import com.android.server.SystemService; |
| import com.android.server.UiThread; |
| import com.android.server.soundtrigger.SoundTriggerInternal; |
| |
| import java.io.FileDescriptor; |
| import java.io.PrintWriter; |
| import java.util.List; |
| import java.util.TreeSet; |
| |
| /** |
| * SystemService that publishes an IVoiceInteractionManagerService. |
| */ |
| public class VoiceInteractionManagerService extends SystemService { |
| static final String TAG = "VoiceInteractionManagerService"; |
| static final boolean DEBUG = false; |
| |
| final Context mContext; |
| final ContentResolver mResolver; |
| final DatabaseHelper mDbHelper; |
| final ActivityManagerInternal mAmInternal; |
| final UserManager mUserManager; |
| final ArraySet<Integer> mLoadedKeyphraseIds = new ArraySet<>(); |
| ShortcutServiceInternal mShortcutServiceInternal; |
| SoundTriggerInternal mSoundTriggerInternal; |
| |
| private final RemoteCallbackList<IVoiceInteractionSessionListener> |
| mVoiceInteractionSessionListeners = new RemoteCallbackList<>(); |
| |
| public VoiceInteractionManagerService(Context context) { |
| super(context); |
| mContext = context; |
| mResolver = context.getContentResolver(); |
| mDbHelper = new DatabaseHelper(context); |
| mServiceStub = new VoiceInteractionManagerServiceStub(); |
| mAmInternal = Preconditions.checkNotNull( |
| LocalServices.getService(ActivityManagerInternal.class)); |
| mUserManager = Preconditions.checkNotNull( |
| context.getSystemService(UserManager.class)); |
| |
| PackageManagerInternal packageManagerInternal = LocalServices.getService( |
| PackageManagerInternal.class); |
| packageManagerInternal.setVoiceInteractionPackagesProvider( |
| new PackageManagerInternal.PackagesProvider() { |
| @Override |
| public String[] getPackages(int userId) { |
| mServiceStub.initForUser(userId); |
| ComponentName interactor = mServiceStub.getCurInteractor(userId); |
| if (interactor != null) { |
| return new String[] {interactor.getPackageName()}; |
| } |
| return null; |
| } |
| }); |
| } |
| |
| @Override |
| public void onStart() { |
| publishBinderService(Context.VOICE_INTERACTION_MANAGER_SERVICE, mServiceStub); |
| publishLocalService(VoiceInteractionManagerInternal.class, new LocalService()); |
| } |
| |
| @Override |
| public void onBootPhase(int phase) { |
| if (PHASE_SYSTEM_SERVICES_READY == phase) { |
| mShortcutServiceInternal = Preconditions.checkNotNull( |
| LocalServices.getService(ShortcutServiceInternal.class)); |
| mSoundTriggerInternal = LocalServices.getService(SoundTriggerInternal.class); |
| } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) { |
| mServiceStub.systemRunning(isSafeMode()); |
| } |
| } |
| |
| @Override |
| public void onStartUser(int userHandle) { |
| mServiceStub.initForUser(userHandle); |
| } |
| |
| @Override |
| public void onUnlockUser(int userHandle) { |
| mServiceStub.initForUser(userHandle); |
| mServiceStub.switchImplementationIfNeeded(false); |
| } |
| |
| @Override |
| public void onSwitchUser(int userHandle) { |
| mServiceStub.switchUser(userHandle); |
| } |
| |
| class LocalService extends VoiceInteractionManagerInternal { |
| @Override |
| public void startLocalVoiceInteraction(IBinder callingActivity, Bundle options) { |
| if (DEBUG) { |
| Slog.i(TAG, "startLocalVoiceInteraction " + callingActivity); |
| } |
| VoiceInteractionManagerService.this.mServiceStub.startLocalVoiceInteraction( |
| callingActivity, options); |
| } |
| |
| @Override |
| public boolean supportsLocalVoiceInteraction() { |
| return VoiceInteractionManagerService.this.mServiceStub.supportsLocalVoiceInteraction(); |
| } |
| |
| @Override |
| public void stopLocalVoiceInteraction(IBinder callingActivity) { |
| if (DEBUG) { |
| Slog.i(TAG, "stopLocalVoiceInteraction " + callingActivity); |
| } |
| VoiceInteractionManagerService.this.mServiceStub.stopLocalVoiceInteraction( |
| callingActivity); |
| } |
| } |
| |
| // implementation entry point and binder service |
| private final VoiceInteractionManagerServiceStub mServiceStub; |
| |
| class VoiceInteractionManagerServiceStub extends IVoiceInteractionManagerService.Stub { |
| |
| VoiceInteractionManagerServiceImpl mImpl; |
| |
| private boolean mSafeMode; |
| private int mCurUser; |
| private boolean mCurUserUnlocked; |
| private final boolean mEnableService; |
| |
| VoiceInteractionManagerServiceStub() { |
| mEnableService = shouldEnableService(mContext); |
| } |
| |
| // TODO: VI Make sure the caller is the current user or profile |
| void startLocalVoiceInteraction(final IBinder token, Bundle options) { |
| if (mImpl == null) return; |
| |
| final long caller = Binder.clearCallingIdentity(); |
| try { |
| mImpl.showSessionLocked(options, |
| VoiceInteractionSession.SHOW_SOURCE_ACTIVITY, |
| new IVoiceInteractionSessionShowCallback.Stub() { |
| @Override |
| public void onFailed() { |
| } |
| |
| @Override |
| public void onShown() { |
| mAmInternal.onLocalVoiceInteractionStarted(token, |
| mImpl.mActiveSession.mSession, |
| mImpl.mActiveSession.mInteractor); |
| } |
| }, |
| token); |
| } finally { |
| Binder.restoreCallingIdentity(caller); |
| } |
| } |
| |
| public void stopLocalVoiceInteraction(IBinder callingActivity) { |
| if (mImpl == null) return; |
| |
| final long caller = Binder.clearCallingIdentity(); |
| try { |
| mImpl.finishLocked(callingActivity, true); |
| } finally { |
| Binder.restoreCallingIdentity(caller); |
| } |
| } |
| |
| public boolean supportsLocalVoiceInteraction() { |
| if (mImpl == null) return false; |
| |
| return mImpl.supportsLocalVoiceInteraction(); |
| } |
| |
| @Override |
| public boolean onTransact(int code, Parcel data, Parcel reply, int flags) |
| throws RemoteException { |
| try { |
| return super.onTransact(code, data, reply, flags); |
| } catch (RuntimeException e) { |
| // The activity manager only throws security exceptions, so let's |
| // log all others. |
| if (!(e instanceof SecurityException)) { |
| Slog.wtf(TAG, "VoiceInteractionManagerService Crash", e); |
| } |
| throw e; |
| } |
| } |
| |
| public void initForUser(int userHandle) { |
| if (DEBUG) Slog.d(TAG, "**************** initForUser user=" + userHandle); |
| String curInteractorStr = Settings.Secure.getStringForUser( |
| mContext.getContentResolver(), |
| Settings.Secure.VOICE_INTERACTION_SERVICE, userHandle); |
| ComponentName curRecognizer = getCurRecognizer(userHandle); |
| VoiceInteractionServiceInfo curInteractorInfo = null; |
| if (DEBUG) Slog.d(TAG, "curInteractorStr=" + curInteractorStr |
| + " curRecognizer=" + curRecognizer); |
| if (curInteractorStr == null && curRecognizer != null && mEnableService) { |
| // If there is no interactor setting, that means we are upgrading |
| // from an older platform version. If the current recognizer is not |
| // set or matches the preferred recognizer, then we want to upgrade |
| // the user to have the default voice interaction service enabled. |
| // Note that we don't do this for low-RAM devices, since we aren't |
| // supporting voice interaction services there. |
| curInteractorInfo = findAvailInteractor(userHandle, curRecognizer.getPackageName()); |
| if (curInteractorInfo != null) { |
| // Looks good! We'll apply this one. To make it happen, we clear the |
| // recognizer so that we don't think we have anything set and will |
| // re-apply the settings. |
| if (DEBUG) Slog.d(TAG, "No set interactor, found avail: " |
| + curInteractorInfo.getServiceInfo().name); |
| curRecognizer = null; |
| } |
| } |
| |
| // If forceInteractorPackage exists, try to apply the interactor from this package if |
| // possible and ignore the regular interactor setting. |
| String forceInteractorPackage = |
| getForceVoiceInteractionServicePackage(mContext.getResources()); |
| if (forceInteractorPackage != null) { |
| curInteractorInfo = findAvailInteractor(userHandle, forceInteractorPackage); |
| if (curInteractorInfo != null) { |
| // We'll apply this one. Clear the recognizer and re-apply the settings. |
| curRecognizer = null; |
| } |
| } |
| |
| // If we are on a svelte device, make sure an interactor is not currently |
| // enabled; if it is, turn it off. |
| if (!mEnableService && curInteractorStr != null) { |
| if (!TextUtils.isEmpty(curInteractorStr)) { |
| if (DEBUG) Slog.d(TAG, "Svelte device; disabling interactor"); |
| setCurInteractor(null, userHandle); |
| curInteractorStr = ""; |
| } |
| } |
| |
| if (curRecognizer != null) { |
| // If we already have at least a recognizer, then we probably want to |
| // leave things as they are... unless something has disappeared. |
| IPackageManager pm = AppGlobals.getPackageManager(); |
| ServiceInfo interactorInfo = null; |
| ServiceInfo recognizerInfo = null; |
| ComponentName curInteractor = !TextUtils.isEmpty(curInteractorStr) |
| ? ComponentName.unflattenFromString(curInteractorStr) : null; |
| try { |
| recognizerInfo = pm.getServiceInfo(curRecognizer, |
| PackageManager.MATCH_DIRECT_BOOT_AWARE |
| | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userHandle); |
| if (curInteractor != null) { |
| interactorInfo = pm.getServiceInfo(curInteractor, |
| PackageManager.MATCH_DIRECT_BOOT_AWARE |
| | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userHandle); |
| } |
| } catch (RemoteException e) { |
| } |
| // If the apps for the currently set components still exist, then all is okay. |
| if (recognizerInfo != null && (curInteractor == null || interactorInfo != null)) { |
| if (DEBUG) Slog.d(TAG, "Current interactor/recognizer okay, done!"); |
| return; |
| } |
| if (DEBUG) Slog.d(TAG, "Bad recognizer (" + recognizerInfo + ") or interactor (" |
| + interactorInfo + ")"); |
| } |
| |
| // Initializing settings, look for an interactor first (but only on non-svelte). |
| if (curInteractorInfo == null && mEnableService) { |
| curInteractorInfo = findAvailInteractor(userHandle, null); |
| } |
| |
| if (curInteractorInfo != null) { |
| // Eventually it will be an error to not specify this. |
| setCurInteractor(new ComponentName(curInteractorInfo.getServiceInfo().packageName, |
| curInteractorInfo.getServiceInfo().name), userHandle); |
| if (curInteractorInfo.getRecognitionService() != null) { |
| setCurRecognizer( |
| new ComponentName(curInteractorInfo.getServiceInfo().packageName, |
| curInteractorInfo.getRecognitionService()), userHandle); |
| return; |
| } |
| } |
| |
| // No voice interactor, we'll just set up a simple recognizer. |
| curRecognizer = findAvailRecognizer(null, userHandle); |
| if (curRecognizer != null) { |
| if (curInteractorInfo == null) { |
| setCurInteractor(null, userHandle); |
| } |
| setCurRecognizer(curRecognizer, userHandle); |
| } |
| } |
| |
| private boolean shouldEnableService(Context context) { |
| // VoiceInteractionService should not be enabled on any low RAM devices |
| // or devices that have not declared the recognition feature, unless the |
| // device's configuration has explicitly set the config flag for a fixed |
| // voice interaction service. |
| return (!ActivityManager.isLowRamDeviceStatic() |
| && context.getPackageManager().hasSystemFeature( |
| PackageManager.FEATURE_VOICE_RECOGNIZERS)) || |
| getForceVoiceInteractionServicePackage(context.getResources()) != null; |
| } |
| |
| private String getForceVoiceInteractionServicePackage(Resources res) { |
| String interactorPackage = |
| res.getString(com.android.internal.R.string.config_forceVoiceInteractionServicePackage); |
| return TextUtils.isEmpty(interactorPackage) ? null : interactorPackage; |
| } |
| |
| public void systemRunning(boolean safeMode) { |
| mSafeMode = safeMode; |
| |
| mPackageMonitor.register(mContext, BackgroundThread.getHandler().getLooper(), |
| UserHandle.ALL, true); |
| new SettingsObserver(UiThread.getHandler()); |
| |
| synchronized (this) { |
| mCurUser = ActivityManager.getCurrentUser(); |
| switchImplementationIfNeededLocked(false); |
| } |
| } |
| |
| public void switchUser(int userHandle) { |
| synchronized (this) { |
| mCurUser = userHandle; |
| mCurUserUnlocked = false; |
| switchImplementationIfNeededLocked(false); |
| } |
| } |
| |
| void switchImplementationIfNeeded(boolean force) { |
| synchronized (this) { |
| switchImplementationIfNeededLocked(force); |
| } |
| } |
| |
| void switchImplementationIfNeededLocked(boolean force) { |
| if (!mSafeMode) { |
| String curService = Settings.Secure.getStringForUser( |
| mResolver, Settings.Secure.VOICE_INTERACTION_SERVICE, mCurUser); |
| ComponentName serviceComponent = null; |
| ServiceInfo serviceInfo = null; |
| if (curService != null && !curService.isEmpty()) { |
| try { |
| serviceComponent = ComponentName.unflattenFromString(curService); |
| serviceInfo = AppGlobals.getPackageManager() |
| .getServiceInfo(serviceComponent, 0, mCurUser); |
| } catch (RuntimeException | RemoteException e) { |
| Slog.wtf(TAG, "Bad voice interaction service name " + curService, e); |
| serviceComponent = null; |
| serviceInfo = null; |
| } |
| } |
| |
| final boolean hasComponent = serviceComponent != null && serviceInfo != null; |
| |
| if (mUserManager.isUserUnlockingOrUnlocked(mCurUser)) { |
| if (hasComponent) { |
| mShortcutServiceInternal.setShortcutHostPackage(TAG, |
| serviceComponent.getPackageName(), mCurUser); |
| } else { |
| mShortcutServiceInternal.setShortcutHostPackage(TAG, null, mCurUser); |
| } |
| } |
| |
| if (force || mImpl == null || mImpl.mUser != mCurUser |
| || !mImpl.mComponent.equals(serviceComponent)) { |
| unloadAllKeyphraseModels(); |
| if (mImpl != null) { |
| mImpl.shutdownLocked(); |
| } |
| if (hasComponent) { |
| mImpl = new VoiceInteractionManagerServiceImpl(mContext, |
| UiThread.getHandler(), this, mCurUser, serviceComponent); |
| mImpl.startLocked(); |
| } else { |
| mImpl = null; |
| } |
| } |
| } |
| } |
| |
| VoiceInteractionServiceInfo findAvailInteractor(int userHandle, String packageName) { |
| List<ResolveInfo> available = |
| mContext.getPackageManager().queryIntentServicesAsUser( |
| new Intent(VoiceInteractionService.SERVICE_INTERFACE), |
| PackageManager.MATCH_DIRECT_BOOT_AWARE |
| | PackageManager.MATCH_DIRECT_BOOT_UNAWARE |
| | PackageManager.MATCH_DEBUG_TRIAGED_MISSING, userHandle); |
| int numAvailable = available.size(); |
| |
| if (numAvailable == 0) { |
| Slog.w(TAG, "no available voice interaction services found for user " + userHandle); |
| return null; |
| } else { |
| // Find first system package. We never want to allow third party services to |
| // be automatically selected, because those require approval of the user. |
| VoiceInteractionServiceInfo foundInfo = null; |
| for (int i=0; i<numAvailable; i++) { |
| ServiceInfo cur = available.get(i).serviceInfo; |
| if ((cur.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) != 0) { |
| ComponentName comp = new ComponentName(cur.packageName, cur.name); |
| try { |
| VoiceInteractionServiceInfo info = new VoiceInteractionServiceInfo( |
| mContext.getPackageManager(), comp, userHandle); |
| if (info.getParseError() == null) { |
| if (packageName == null || info.getServiceInfo().packageName.equals( |
| packageName)) { |
| if (foundInfo == null) { |
| foundInfo = info; |
| } else { |
| Slog.w(TAG, "More than one voice interaction service, " |
| + "picking first " |
| + new ComponentName( |
| foundInfo.getServiceInfo().packageName, |
| foundInfo.getServiceInfo().name) |
| + " over " |
| + new ComponentName(cur.packageName, cur.name)); |
| } |
| } |
| } else { |
| Slog.w(TAG, "Bad interaction service " + comp + ": " |
| + info.getParseError()); |
| } |
| } catch (PackageManager.NameNotFoundException e) { |
| Slog.w(TAG, "Failure looking up interaction service " + comp); |
| } |
| } |
| } |
| |
| return foundInfo; |
| } |
| } |
| |
| ComponentName getCurInteractor(int userHandle) { |
| String curInteractor = Settings.Secure.getStringForUser( |
| mContext.getContentResolver(), |
| Settings.Secure.VOICE_INTERACTION_SERVICE, userHandle); |
| if (TextUtils.isEmpty(curInteractor)) { |
| return null; |
| } |
| if (DEBUG) Slog.d(TAG, "getCurInteractor curInteractor=" + curInteractor |
| + " user=" + userHandle); |
| return ComponentName.unflattenFromString(curInteractor); |
| } |
| |
| void setCurInteractor(ComponentName comp, int userHandle) { |
| Settings.Secure.putStringForUser(mContext.getContentResolver(), |
| Settings.Secure.VOICE_INTERACTION_SERVICE, |
| comp != null ? comp.flattenToShortString() : "", userHandle); |
| if (DEBUG) Slog.d(TAG, "setCurInteractor comp=" + comp |
| + " user=" + userHandle); |
| } |
| |
| ComponentName findAvailRecognizer(String prefPackage, int userHandle) { |
| List<ResolveInfo> available = |
| mContext.getPackageManager().queryIntentServicesAsUser( |
| new Intent(RecognitionService.SERVICE_INTERFACE), |
| PackageManager.MATCH_DIRECT_BOOT_AWARE |
| | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userHandle); |
| int numAvailable = available.size(); |
| |
| if (numAvailable == 0) { |
| Slog.w(TAG, "no available voice recognition services found for user " + userHandle); |
| return null; |
| } else { |
| if (prefPackage != null) { |
| for (int i=0; i<numAvailable; i++) { |
| ServiceInfo serviceInfo = available.get(i).serviceInfo; |
| if (prefPackage.equals(serviceInfo.packageName)) { |
| return new ComponentName(serviceInfo.packageName, serviceInfo.name); |
| } |
| } |
| } |
| if (numAvailable > 1) { |
| Slog.w(TAG, "more than one voice recognition service found, picking first"); |
| } |
| |
| ServiceInfo serviceInfo = available.get(0).serviceInfo; |
| return new ComponentName(serviceInfo.packageName, serviceInfo.name); |
| } |
| } |
| |
| ComponentName getCurRecognizer(int userHandle) { |
| String curRecognizer = Settings.Secure.getStringForUser( |
| mContext.getContentResolver(), |
| Settings.Secure.VOICE_RECOGNITION_SERVICE, userHandle); |
| if (TextUtils.isEmpty(curRecognizer)) { |
| return null; |
| } |
| if (DEBUG) Slog.d(TAG, "getCurRecognizer curRecognizer=" + curRecognizer |
| + " user=" + userHandle); |
| return ComponentName.unflattenFromString(curRecognizer); |
| } |
| |
| void setCurRecognizer(ComponentName comp, int userHandle) { |
| Settings.Secure.putStringForUser(mContext.getContentResolver(), |
| Settings.Secure.VOICE_RECOGNITION_SERVICE, |
| comp != null ? comp.flattenToShortString() : "", userHandle); |
| if (DEBUG) Slog.d(TAG, "setCurRecognizer comp=" + comp |
| + " user=" + userHandle); |
| } |
| |
| ComponentName getCurAssistant(int userHandle) { |
| String curAssistant = Settings.Secure.getStringForUser( |
| mContext.getContentResolver(), |
| Settings.Secure.ASSISTANT, userHandle); |
| if (TextUtils.isEmpty(curAssistant)) { |
| return null; |
| } |
| if (DEBUG) Slog.d(TAG, "getCurAssistant curAssistant=" + curAssistant |
| + " user=" + userHandle); |
| return ComponentName.unflattenFromString(curAssistant); |
| } |
| |
| void resetCurAssistant(int userHandle) { |
| Settings.Secure.putStringForUser(mContext.getContentResolver(), |
| Settings.Secure.ASSISTANT, null, userHandle); |
| } |
| |
| @Override |
| public void showSession(IVoiceInteractionService service, Bundle args, int flags) { |
| synchronized (this) { |
| if (mImpl == null || mImpl.mService == null |
| || service.asBinder() != mImpl.mService.asBinder()) { |
| throw new SecurityException( |
| "Caller is not the current voice interaction service"); |
| } |
| final long caller = Binder.clearCallingIdentity(); |
| try { |
| mImpl.showSessionLocked(args, flags, null, null); |
| } finally { |
| Binder.restoreCallingIdentity(caller); |
| } |
| } |
| } |
| |
| @Override |
| public boolean deliverNewSession(IBinder token, IVoiceInteractionSession session, |
| IVoiceInteractor interactor) { |
| synchronized (this) { |
| if (mImpl == null) { |
| throw new SecurityException( |
| "deliverNewSession without running voice interaction service"); |
| } |
| final long caller = Binder.clearCallingIdentity(); |
| try { |
| return mImpl.deliverNewSessionLocked(token, session, interactor); |
| } finally { |
| Binder.restoreCallingIdentity(caller); |
| } |
| } |
| } |
| |
| @Override |
| public boolean showSessionFromSession(IBinder token, Bundle sessionArgs, int flags) { |
| synchronized (this) { |
| if (mImpl == null) { |
| Slog.w(TAG, "showSessionFromSession without running voice interaction service"); |
| return false; |
| } |
| final long caller = Binder.clearCallingIdentity(); |
| try { |
| return mImpl.showSessionLocked(sessionArgs, flags, null, null); |
| } finally { |
| Binder.restoreCallingIdentity(caller); |
| } |
| } |
| } |
| |
| @Override |
| public boolean hideSessionFromSession(IBinder token) { |
| synchronized (this) { |
| if (mImpl == null) { |
| Slog.w(TAG, "hideSessionFromSession without running voice interaction service"); |
| return false; |
| } |
| final long caller = Binder.clearCallingIdentity(); |
| try { |
| return mImpl.hideSessionLocked(); |
| } finally { |
| Binder.restoreCallingIdentity(caller); |
| } |
| } |
| } |
| |
| @Override |
| public int startVoiceActivity(IBinder token, Intent intent, String resolvedType) { |
| synchronized (this) { |
| if (mImpl == null) { |
| Slog.w(TAG, "startVoiceActivity without running voice interaction service"); |
| return ActivityManager.START_CANCELED; |
| } |
| final int callingPid = Binder.getCallingPid(); |
| final int callingUid = Binder.getCallingUid(); |
| final long caller = Binder.clearCallingIdentity(); |
| try { |
| return mImpl.startVoiceActivityLocked(callingPid, callingUid, token, |
| intent, resolvedType); |
| } finally { |
| Binder.restoreCallingIdentity(caller); |
| } |
| } |
| } |
| |
| @Override |
| public int startAssistantActivity(IBinder token, Intent intent, String resolvedType) { |
| synchronized (this) { |
| if (mImpl == null) { |
| Slog.w(TAG, "startAssistantActivity without running voice interaction service"); |
| return ActivityManager.START_CANCELED; |
| } |
| final int callingPid = Binder.getCallingPid(); |
| final int callingUid = Binder.getCallingUid(); |
| final long caller = Binder.clearCallingIdentity(); |
| try { |
| return mImpl.startAssistantActivityLocked(callingPid, callingUid, token, |
| intent, resolvedType); |
| } finally { |
| Binder.restoreCallingIdentity(caller); |
| } |
| } |
| } |
| |
| @Override |
| public void setKeepAwake(IBinder token, boolean keepAwake) { |
| synchronized (this) { |
| if (mImpl == null) { |
| Slog.w(TAG, "setKeepAwake without running voice interaction service"); |
| return; |
| } |
| final long caller = Binder.clearCallingIdentity(); |
| try { |
| mImpl.setKeepAwakeLocked(token, keepAwake); |
| } finally { |
| Binder.restoreCallingIdentity(caller); |
| } |
| } |
| } |
| |
| @Override |
| public void closeSystemDialogs(IBinder token) { |
| synchronized (this) { |
| if (mImpl == null) { |
| Slog.w(TAG, "closeSystemDialogs without running voice interaction service"); |
| return; |
| } |
| final long caller = Binder.clearCallingIdentity(); |
| try { |
| mImpl.closeSystemDialogsLocked(token); |
| } finally { |
| Binder.restoreCallingIdentity(caller); |
| } |
| } |
| } |
| |
| @Override |
| public void finish(IBinder token) { |
| synchronized (this) { |
| if (mImpl == null) { |
| Slog.w(TAG, "finish without running voice interaction service"); |
| return; |
| } |
| final long caller = Binder.clearCallingIdentity(); |
| try { |
| mImpl.finishLocked(token, false); |
| } finally { |
| Binder.restoreCallingIdentity(caller); |
| } |
| } |
| } |
| |
| @Override |
| public void setDisabledShowContext(int flags) { |
| synchronized (this) { |
| if (mImpl == null) { |
| Slog.w(TAG, "setDisabledShowContext without running voice interaction service"); |
| return; |
| } |
| final int callingUid = Binder.getCallingUid(); |
| final long caller = Binder.clearCallingIdentity(); |
| try { |
| mImpl.setDisabledShowContextLocked(callingUid, flags); |
| } finally { |
| Binder.restoreCallingIdentity(caller); |
| } |
| } |
| } |
| |
| @Override |
| public int getDisabledShowContext() { |
| synchronized (this) { |
| if (mImpl == null) { |
| Slog.w(TAG, "getDisabledShowContext without running voice interaction service"); |
| return 0; |
| } |
| final int callingUid = Binder.getCallingUid(); |
| final long caller = Binder.clearCallingIdentity(); |
| try { |
| return mImpl.getDisabledShowContextLocked(callingUid); |
| } finally { |
| Binder.restoreCallingIdentity(caller); |
| } |
| } |
| } |
| |
| @Override |
| public int getUserDisabledShowContext() { |
| synchronized (this) { |
| if (mImpl == null) { |
| Slog.w(TAG, |
| "getUserDisabledShowContext without running voice interaction service"); |
| return 0; |
| } |
| final int callingUid = Binder.getCallingUid(); |
| final long caller = Binder.clearCallingIdentity(); |
| try { |
| return mImpl.getUserDisabledShowContextLocked(callingUid); |
| } finally { |
| Binder.restoreCallingIdentity(caller); |
| } |
| } |
| } |
| |
| //----------------- Model management APIs --------------------------------// |
| |
| @Override |
| public KeyphraseSoundModel getKeyphraseSoundModel(int keyphraseId, String bcp47Locale) { |
| enforceCallingPermission(Manifest.permission.MANAGE_VOICE_KEYPHRASES); |
| |
| if (bcp47Locale == null) { |
| throw new IllegalArgumentException("Illegal argument(s) in getKeyphraseSoundModel"); |
| } |
| |
| final int callingUid = UserHandle.getCallingUserId(); |
| final long caller = Binder.clearCallingIdentity(); |
| try { |
| return mDbHelper.getKeyphraseSoundModel(keyphraseId, callingUid, bcp47Locale); |
| } finally { |
| Binder.restoreCallingIdentity(caller); |
| } |
| } |
| |
| @Override |
| public int updateKeyphraseSoundModel(KeyphraseSoundModel model) { |
| enforceCallingPermission(Manifest.permission.MANAGE_VOICE_KEYPHRASES); |
| if (model == null) { |
| throw new IllegalArgumentException("Model must not be null"); |
| } |
| |
| final long caller = Binder.clearCallingIdentity(); |
| try { |
| if (mDbHelper.updateKeyphraseSoundModel(model)) { |
| synchronized (this) { |
| // Notify the voice interaction service of a change in sound models. |
| if (mImpl != null && mImpl.mService != null) { |
| mImpl.notifySoundModelsChangedLocked(); |
| } |
| } |
| return SoundTriggerInternal.STATUS_OK; |
| } else { |
| return SoundTriggerInternal.STATUS_ERROR; |
| } |
| } finally { |
| Binder.restoreCallingIdentity(caller); |
| } |
| } |
| |
| @Override |
| public int deleteKeyphraseSoundModel(int keyphraseId, String bcp47Locale) { |
| enforceCallingPermission(Manifest.permission.MANAGE_VOICE_KEYPHRASES); |
| |
| if (bcp47Locale == null) { |
| throw new IllegalArgumentException( |
| "Illegal argument(s) in deleteKeyphraseSoundModel"); |
| } |
| |
| final int callingUid = UserHandle.getCallingUserId(); |
| final long caller = Binder.clearCallingIdentity(); |
| boolean deleted = false; |
| try { |
| int unloadStatus = mSoundTriggerInternal.unloadKeyphraseModel(keyphraseId); |
| if (unloadStatus != SoundTriggerInternal.STATUS_OK) { |
| Slog.w(TAG, "Unable to unload keyphrase sound model:" + unloadStatus); |
| } |
| deleted = mDbHelper.deleteKeyphraseSoundModel(keyphraseId, callingUid, bcp47Locale); |
| return deleted ? SoundTriggerInternal.STATUS_OK : SoundTriggerInternal.STATUS_ERROR; |
| } finally { |
| if (deleted) { |
| synchronized (this) { |
| // Notify the voice interaction service of a change in sound models. |
| if (mImpl != null && mImpl.mService != null) { |
| mImpl.notifySoundModelsChangedLocked(); |
| } |
| mLoadedKeyphraseIds.remove(keyphraseId); |
| } |
| } |
| Binder.restoreCallingIdentity(caller); |
| } |
| } |
| |
| //----------------- SoundTrigger APIs --------------------------------// |
| @Override |
| public boolean isEnrolledForKeyphrase(IVoiceInteractionService service, int keyphraseId, |
| String bcp47Locale) { |
| synchronized (this) { |
| if (mImpl == null || mImpl.mService == null |
| || service.asBinder() != mImpl.mService.asBinder()) { |
| throw new SecurityException( |
| "Caller is not the current voice interaction service"); |
| } |
| } |
| |
| if (bcp47Locale == null) { |
| throw new IllegalArgumentException("Illegal argument(s) in isEnrolledForKeyphrase"); |
| } |
| |
| final int callingUid = UserHandle.getCallingUserId(); |
| final long caller = Binder.clearCallingIdentity(); |
| try { |
| KeyphraseSoundModel model = |
| mDbHelper.getKeyphraseSoundModel(keyphraseId, callingUid, bcp47Locale); |
| return model != null; |
| } finally { |
| Binder.restoreCallingIdentity(caller); |
| } |
| } |
| |
| @Override |
| public ModuleProperties getDspModuleProperties(IVoiceInteractionService service) { |
| // Allow the call if this is the current voice interaction service. |
| synchronized (this) { |
| if (mImpl == null || mImpl.mService == null |
| || service == null || service.asBinder() != mImpl.mService.asBinder()) { |
| throw new SecurityException( |
| "Caller is not the current voice interaction service"); |
| } |
| |
| final long caller = Binder.clearCallingIdentity(); |
| try { |
| return mSoundTriggerInternal.getModuleProperties(); |
| } finally { |
| Binder.restoreCallingIdentity(caller); |
| } |
| } |
| } |
| |
| @Override |
| public int startRecognition(IVoiceInteractionService service, int keyphraseId, |
| String bcp47Locale, IRecognitionStatusCallback callback, |
| RecognitionConfig recognitionConfig) { |
| // Allow the call if this is the current voice interaction service. |
| synchronized (this) { |
| if (mImpl == null || mImpl.mService == null |
| || service == null || service.asBinder() != mImpl.mService.asBinder()) { |
| throw new SecurityException( |
| "Caller is not the current voice interaction service"); |
| } |
| |
| if (callback == null || recognitionConfig == null || bcp47Locale == null) { |
| throw new IllegalArgumentException("Illegal argument(s) in startRecognition"); |
| } |
| } |
| |
| int callingUid = UserHandle.getCallingUserId(); |
| final long caller = Binder.clearCallingIdentity(); |
| try { |
| KeyphraseSoundModel soundModel = |
| mDbHelper.getKeyphraseSoundModel(keyphraseId, callingUid, bcp47Locale); |
| if (soundModel == null |
| || soundModel.uuid == null |
| || soundModel.keyphrases == null) { |
| Slog.w(TAG, "No matching sound model found in startRecognition"); |
| return SoundTriggerInternal.STATUS_ERROR; |
| } else { |
| // Regardless of the status of the start recognition, we need to make sure |
| // that we unload this model if needed later. |
| synchronized (this) { |
| mLoadedKeyphraseIds.add(keyphraseId); |
| } |
| return mSoundTriggerInternal.startRecognition( |
| keyphraseId, soundModel, callback, recognitionConfig); |
| } |
| } finally { |
| Binder.restoreCallingIdentity(caller); |
| } |
| } |
| |
| @Override |
| public int stopRecognition(IVoiceInteractionService service, int keyphraseId, |
| IRecognitionStatusCallback callback) { |
| // Allow the call if this is the current voice interaction service. |
| synchronized (this) { |
| if (mImpl == null || mImpl.mService == null |
| || service == null || service.asBinder() != mImpl.mService.asBinder()) { |
| throw new SecurityException( |
| "Caller is not the current voice interaction service"); |
| } |
| } |
| |
| final long caller = Binder.clearCallingIdentity(); |
| try { |
| return mSoundTriggerInternal.stopRecognition(keyphraseId, callback); |
| } finally { |
| Binder.restoreCallingIdentity(caller); |
| } |
| } |
| |
| private synchronized void unloadAllKeyphraseModels() { |
| for (int i = 0; i < mLoadedKeyphraseIds.size(); i++) { |
| final long caller = Binder.clearCallingIdentity(); |
| try { |
| int status = mSoundTriggerInternal.unloadKeyphraseModel( |
| mLoadedKeyphraseIds.valueAt(i)); |
| if (status != SoundTriggerInternal.STATUS_OK) { |
| Slog.w(TAG, "Failed to unload keyphrase " + mLoadedKeyphraseIds.valueAt(i) |
| + ":" + status); |
| } |
| } finally { |
| Binder.restoreCallingIdentity(caller); |
| } |
| } |
| mLoadedKeyphraseIds.clear(); |
| } |
| |
| @Override |
| public ComponentName getActiveServiceComponentName() { |
| enforceCallingPermission(Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE); |
| synchronized (this) { |
| return mImpl != null ? mImpl.mComponent : null; |
| } |
| } |
| |
| @Override |
| public boolean showSessionForActiveService(Bundle args, int sourceFlags, |
| IVoiceInteractionSessionShowCallback showCallback, IBinder activityToken) { |
| enforceCallingPermission(Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE); |
| synchronized (this) { |
| if (mImpl == null) { |
| Slog.w(TAG, "showSessionForActiveService without running voice interaction" |
| + "service"); |
| return false; |
| } |
| final long caller = Binder.clearCallingIdentity(); |
| try { |
| return mImpl.showSessionLocked(args, |
| sourceFlags |
| | VoiceInteractionSession.SHOW_WITH_ASSIST |
| | VoiceInteractionSession.SHOW_WITH_SCREENSHOT, |
| showCallback, activityToken); |
| } finally { |
| Binder.restoreCallingIdentity(caller); |
| } |
| } |
| } |
| |
| @Override |
| public void hideCurrentSession() throws RemoteException { |
| enforceCallingPermission(Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE); |
| synchronized (this) { |
| if (mImpl == null) { |
| return; |
| } |
| final long caller = Binder.clearCallingIdentity(); |
| try { |
| if (mImpl.mActiveSession != null && mImpl.mActiveSession.mSession != null) { |
| try { |
| mImpl.mActiveSession.mSession.closeSystemDialogs(); |
| } catch (RemoteException e) { |
| Log.w(TAG, "Failed to call closeSystemDialogs", e); |
| } |
| } |
| } finally { |
| Binder.restoreCallingIdentity(caller); |
| } |
| } |
| } |
| |
| @Override |
| public void launchVoiceAssistFromKeyguard() { |
| enforceCallingPermission(Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE); |
| synchronized (this) { |
| if (mImpl == null) { |
| Slog.w(TAG, "launchVoiceAssistFromKeyguard without running voice interaction" |
| + "service"); |
| return; |
| } |
| final long caller = Binder.clearCallingIdentity(); |
| try { |
| mImpl.launchVoiceAssistFromKeyguard(); |
| } finally { |
| Binder.restoreCallingIdentity(caller); |
| } |
| } |
| } |
| |
| @Override |
| public boolean isSessionRunning() { |
| enforceCallingPermission(Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE); |
| synchronized (this) { |
| return mImpl != null && mImpl.mActiveSession != null; |
| } |
| } |
| |
| @Override |
| public boolean activeServiceSupportsAssist() { |
| enforceCallingPermission(Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE); |
| synchronized (this) { |
| return mImpl != null && mImpl.mInfo != null && mImpl.mInfo.getSupportsAssist(); |
| } |
| } |
| |
| @Override |
| public boolean activeServiceSupportsLaunchFromKeyguard() throws RemoteException { |
| enforceCallingPermission(Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE); |
| synchronized (this) { |
| return mImpl != null && mImpl.mInfo != null |
| && mImpl.mInfo.getSupportsLaunchFromKeyguard(); |
| } |
| } |
| |
| @Override |
| public void onLockscreenShown() { |
| enforceCallingPermission(Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE); |
| synchronized (this) { |
| if (mImpl == null) { |
| return; |
| } |
| final long caller = Binder.clearCallingIdentity(); |
| try { |
| if (mImpl.mActiveSession != null && mImpl.mActiveSession.mSession != null) { |
| try { |
| mImpl.mActiveSession.mSession.onLockscreenShown(); |
| } catch (RemoteException e) { |
| Log.w(TAG, "Failed to call onLockscreenShown", e); |
| } |
| } |
| } finally { |
| Binder.restoreCallingIdentity(caller); |
| } |
| } |
| } |
| |
| @Override |
| public void registerVoiceInteractionSessionListener( |
| IVoiceInteractionSessionListener listener) { |
| enforceCallingPermission(Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE); |
| synchronized (this) { |
| mVoiceInteractionSessionListeners.register(listener); |
| } |
| } |
| |
| public void onSessionShown() { |
| synchronized (this) { |
| final int size = mVoiceInteractionSessionListeners.beginBroadcast(); |
| for (int i = 0; i < size; ++i) { |
| final IVoiceInteractionSessionListener listener = |
| mVoiceInteractionSessionListeners.getBroadcastItem(i); |
| try { |
| listener.onVoiceSessionShown(); |
| } catch (RemoteException e) { |
| Slog.e(TAG, "Error delivering voice interaction open event.", e); |
| } |
| } |
| mVoiceInteractionSessionListeners.finishBroadcast(); |
| } |
| } |
| |
| public void onSessionHidden() { |
| synchronized (this) { |
| final int size = mVoiceInteractionSessionListeners.beginBroadcast(); |
| for (int i = 0; i < size; ++i) { |
| final IVoiceInteractionSessionListener listener = |
| mVoiceInteractionSessionListeners.getBroadcastItem(i); |
| try { |
| listener.onVoiceSessionHidden(); |
| |
| } catch (RemoteException e) { |
| Slog.e(TAG, "Error delivering voice interaction closed event.", e); |
| } |
| } |
| mVoiceInteractionSessionListeners.finishBroadcast(); |
| } |
| } |
| |
| @Override |
| public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { |
| if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; |
| synchronized (this) { |
| pw.println("VOICE INTERACTION MANAGER (dumpsys voiceinteraction)"); |
| pw.println(" mEnableService: " + mEnableService); |
| if (mImpl == null) { |
| pw.println(" (No active implementation)"); |
| return; |
| } |
| mImpl.dumpLocked(fd, pw, args); |
| } |
| mSoundTriggerInternal.dump(fd, pw, args); |
| } |
| |
| private void enforceCallingPermission(String permission) { |
| if (mContext.checkCallingOrSelfPermission(permission) |
| != PackageManager.PERMISSION_GRANTED) { |
| throw new SecurityException("Caller does not hold the permission " + permission); |
| } |
| } |
| |
| class SettingsObserver extends ContentObserver { |
| SettingsObserver(Handler handler) { |
| super(handler); |
| ContentResolver resolver = mContext.getContentResolver(); |
| resolver.registerContentObserver(Settings.Secure.getUriFor( |
| Settings.Secure.VOICE_INTERACTION_SERVICE), false, this, |
| UserHandle.USER_ALL); |
| } |
| |
| @Override public void onChange(boolean selfChange) { |
| synchronized (VoiceInteractionManagerServiceStub.this) { |
| switchImplementationIfNeededLocked(false); |
| } |
| } |
| } |
| |
| PackageMonitor mPackageMonitor = new PackageMonitor() { |
| @Override |
| public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) { |
| if (DEBUG) Slog.d(TAG, "onHandleForceStop uid=" + uid + " doit=" + doit); |
| |
| int userHandle = UserHandle.getUserId(uid); |
| ComponentName curInteractor = getCurInteractor(userHandle); |
| ComponentName curRecognizer = getCurRecognizer(userHandle); |
| boolean hit = false; |
| for (String pkg : packages) { |
| if (curInteractor != null && pkg.equals(curInteractor.getPackageName())) { |
| hit = true; |
| break; |
| } else if (curRecognizer != null |
| && pkg.equals(curRecognizer.getPackageName())) { |
| hit = true; |
| break; |
| } |
| } |
| if (hit && doit) { |
| // The user is force stopping our current interactor/recognizer. |
| // Clear the current settings and restore default state. |
| synchronized (VoiceInteractionManagerServiceStub.this) { |
| unloadAllKeyphraseModels(); |
| if (mImpl != null) { |
| mImpl.shutdownLocked(); |
| mImpl = null; |
| } |
| setCurInteractor(null, userHandle); |
| setCurRecognizer(null, userHandle); |
| resetCurAssistant(userHandle); |
| initForUser(userHandle); |
| switchImplementationIfNeededLocked(true); |
| } |
| } |
| return hit; |
| } |
| |
| @Override |
| public void onHandleUserStop(Intent intent, int userHandle) { |
| } |
| |
| @Override |
| public void onPackageModified(String pkgName) { |
| // If the package modified is not in the current user, then don't bother making |
| // any changes as we are going to do any initialization needed when we switch users. |
| if (mCurUser != getChangingUserId()) { |
| return; |
| } |
| // Package getting updated will be handled by {@link #onSomePackagesChanged}. |
| if (isPackageAppearing(pkgName) != PACKAGE_UNCHANGED) { |
| return; |
| } |
| final ComponentName curInteractor = getCurInteractor(mCurUser); |
| if (curInteractor == null) { |
| final VoiceInteractionServiceInfo availInteractorInfo |
| = findAvailInteractor(mCurUser, pkgName); |
| if (availInteractorInfo != null) { |
| final ComponentName availInteractor = new ComponentName( |
| availInteractorInfo.getServiceInfo().packageName, |
| availInteractorInfo.getServiceInfo().name); |
| setCurInteractor(availInteractor, mCurUser); |
| if (getCurRecognizer(mCurUser) == null && |
| availInteractorInfo.getRecognitionService() != null) { |
| setCurRecognizer(new ComponentName( |
| availInteractorInfo.getServiceInfo().packageName, |
| availInteractorInfo.getRecognitionService()), mCurUser); |
| } |
| } |
| } else { |
| if (didSomePackagesChange()) { |
| // Package is changed |
| if (curInteractor != null && pkgName.equals( |
| curInteractor.getPackageName())) { |
| switchImplementationIfNeeded(true); |
| } |
| } else { |
| // Only some components are changed |
| if (curInteractor != null |
| && isComponentModified(curInteractor.getClassName())) { |
| switchImplementationIfNeeded(true); |
| } |
| } |
| } |
| } |
| |
| @Override |
| public void onSomePackagesChanged() { |
| int userHandle = getChangingUserId(); |
| if (DEBUG) Slog.d(TAG, "onSomePackagesChanged user=" + userHandle); |
| |
| synchronized (VoiceInteractionManagerServiceStub.this) { |
| ComponentName curInteractor = getCurInteractor(userHandle); |
| ComponentName curRecognizer = getCurRecognizer(userHandle); |
| ComponentName curAssistant = getCurAssistant(userHandle); |
| if (curRecognizer == null) { |
| // Could a new recognizer appear when we don't have one pre-installed? |
| if (anyPackagesAppearing()) { |
| curRecognizer = findAvailRecognizer(null, userHandle); |
| if (curRecognizer != null) { |
| setCurRecognizer(curRecognizer, userHandle); |
| } |
| } |
| return; |
| } |
| |
| if (curInteractor != null) { |
| int change = isPackageDisappearing(curInteractor.getPackageName()); |
| if (change == PACKAGE_PERMANENT_CHANGE) { |
| // The currently set interactor is permanently gone; fall back to |
| // the default config. |
| setCurInteractor(null, userHandle); |
| setCurRecognizer(null, userHandle); |
| resetCurAssistant(userHandle); |
| initForUser(userHandle); |
| return; |
| } |
| |
| change = isPackageAppearing(curInteractor.getPackageName()); |
| if (change != PACKAGE_UNCHANGED) { |
| // If current interactor is now appearing, for any reason, then |
| // restart our connection with it. |
| if (mImpl != null && curInteractor.getPackageName().equals( |
| mImpl.mComponent.getPackageName())) { |
| switchImplementationIfNeededLocked(true); |
| } |
| } |
| return; |
| } |
| |
| if (curAssistant != null) { |
| int change = isPackageDisappearing(curAssistant.getPackageName()); |
| if (change == PACKAGE_PERMANENT_CHANGE) { |
| // If the currently set assistant is being removed, then we should |
| // reset back to the default state (which is probably that we prefer |
| // to have the default full voice interactor enabled). |
| setCurInteractor(null, userHandle); |
| setCurRecognizer(null, userHandle); |
| resetCurAssistant(userHandle); |
| initForUser(userHandle); |
| return; |
| } |
| } |
| |
| // There is no interactor, so just deal with a simple recognizer. |
| int change = isPackageDisappearing(curRecognizer.getPackageName()); |
| if (change == PACKAGE_PERMANENT_CHANGE |
| || change == PACKAGE_TEMPORARY_CHANGE) { |
| setCurRecognizer(findAvailRecognizer(null, userHandle), userHandle); |
| |
| } else if (isPackageModified(curRecognizer.getPackageName())) { |
| setCurRecognizer(findAvailRecognizer(curRecognizer.getPackageName(), |
| userHandle), userHandle); |
| } |
| } |
| } |
| }; |
| } |
| } |