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 
17 package com.android.server.voiceinteraction;
18 
19 import static android.app.ActivityManager.START_ASSISTANT_HIDDEN_SESSION;
20 import static android.app.ActivityManager.START_ASSISTANT_NOT_ACTIVE_SESSION;
21 import static android.app.ActivityManager.START_VOICE_HIDDEN_SESSION;
22 import static android.app.ActivityManager.START_VOICE_NOT_ACTIVE_SESSION;
23 import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
24 import static android.service.voice.VoiceInteractionSession.KEY_SHOW_SESSION_ID;
25 
26 import static com.android.server.policy.PhoneWindowManager.SYSTEM_DIALOG_REASON_ASSIST;
27 
28 import android.Manifest;
29 import android.annotation.NonNull;
30 import android.annotation.Nullable;
31 import android.app.ActivityManager;
32 import android.app.ActivityTaskManager;
33 import android.app.AppGlobals;
34 import android.app.ApplicationExitInfo;
35 import android.app.IActivityManager;
36 import android.app.IActivityTaskManager;
37 import android.content.BroadcastReceiver;
38 import android.content.ComponentName;
39 import android.content.Context;
40 import android.content.Intent;
41 import android.content.IntentFilter;
42 import android.content.ServiceConnection;
43 import android.content.pm.PackageManager;
44 import android.content.pm.PackageManagerInternal;
45 import android.content.pm.ParceledListSlice;
46 import android.content.pm.ServiceInfo;
47 import android.hardware.soundtrigger.IRecognitionStatusCallback;
48 import android.hardware.soundtrigger.SoundTrigger;
49 import android.media.AudioFormat;
50 import android.media.permission.Identity;
51 import android.os.Bundle;
52 import android.os.Handler;
53 import android.os.IBinder;
54 import android.os.ParcelFileDescriptor;
55 import android.os.PersistableBundle;
56 import android.os.RemoteCallback;
57 import android.os.RemoteException;
58 import android.os.ServiceManager;
59 import android.os.SharedMemory;
60 import android.os.SystemProperties;
61 import android.os.UserHandle;
62 import android.service.voice.HotwordDetector;
63 import android.service.voice.IMicrophoneHotwordDetectionVoiceInteractionCallback;
64 import android.service.voice.IVisualQueryDetectionVoiceInteractionCallback;
65 import android.service.voice.IVoiceInteractionService;
66 import android.service.voice.IVoiceInteractionSession;
67 import android.service.voice.VoiceInteractionService;
68 import android.service.voice.VoiceInteractionServiceInfo;
69 import android.system.OsConstants;
70 import android.text.TextUtils;
71 import android.util.PrintWriterPrinter;
72 import android.util.Slog;
73 import android.view.IWindowManager;
74 
75 import com.android.internal.app.IHotwordRecognitionStatusCallback;
76 import com.android.internal.app.IVisualQueryDetectionAttentionListener;
77 import com.android.internal.app.IVoiceActionCheckCallback;
78 import com.android.internal.app.IVoiceInteractionSessionShowCallback;
79 import com.android.internal.app.IVoiceInteractor;
80 import com.android.internal.util.function.pooled.PooledLambda;
81 import com.android.server.LocalServices;
82 import com.android.server.wm.ActivityAssistInfo;
83 import com.android.server.wm.ActivityTaskManagerInternal;
84 import com.android.server.wm.ActivityTaskManagerInternal.ActivityTokens;
85 
86 import java.io.FileDescriptor;
87 import java.io.PrintWriter;
88 import java.util.ArrayList;
89 import java.util.List;
90 import java.util.Objects;
91 
92 class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConnection.Callback {
93     final static String TAG = "VoiceInteractionServiceManager";
94     static final boolean DEBUG = false;
95 
96     final static String CLOSE_REASON_VOICE_INTERACTION = "voiceinteraction";
97 
98     /** The delay time for retrying to request DirectActions. */
99     private static final long REQUEST_DIRECT_ACTIONS_RETRY_TIME_MS = 200;
100     private static final boolean SYSPROP_VISUAL_QUERY_SERVICE_ENABLED =
101             SystemProperties.getBoolean("ro.hotword.visual_query_service_enabled", false);
102 
103     final boolean mValid;
104 
105     final Context mContext;
106     final Handler mHandler;
107     final Handler mDirectActionsHandler;
108     final VoiceInteractionManagerService.VoiceInteractionManagerServiceStub mServiceStub;
109     final int mUser;
110     final ComponentName mComponent;
111     final IActivityManager mAm;
112     final IActivityTaskManager mAtm;
113     final PackageManagerInternal mPackageManagerInternal;
114     final VoiceInteractionServiceInfo mInfo;
115     final ComponentName mSessionComponentName;
116     final IWindowManager mIWindowManager;
117     final ComponentName mHotwordDetectionComponentName;
118     final ComponentName mVisualQueryDetectionComponentName;
119     boolean mBound = false;
120     IVoiceInteractionService mService;
121     volatile HotwordDetectionConnection mHotwordDetectionConnection;
122 
123     VoiceInteractionSessionConnection mActiveSession;
124     int mDisabledShowContext;
125 
126     final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
127         @Override
128         public void onReceive(Context context, Intent intent) {
129             if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) {
130                 String reason = intent.getStringExtra("reason");
131                 if (!CLOSE_REASON_VOICE_INTERACTION.equals(reason)
132                         && !TextUtils.equals("dream", reason)
133                         && !SYSTEM_DIALOG_REASON_ASSIST.equals(reason)) {
134                     synchronized (mServiceStub) {
135                         if (mActiveSession != null && mActiveSession.mSession != null) {
136                             try {
137                                 mActiveSession.mSession.closeSystemDialogs();
138                             } catch (RemoteException e) {
139                             }
140                         }
141                     }
142                 }
143             }
144         }
145     };
146 
147     final ServiceConnection mConnection = new ServiceConnection() {
148         @Override
149         public void onServiceConnected(ComponentName name, IBinder service) {
150             if (DEBUG) {
151                 Slog.d(TAG, "onServiceConnected to " + name + " for user(" + mUser + ")");
152             }
153             synchronized (mServiceStub) {
154                 mService = IVoiceInteractionService.Stub.asInterface(service);
155                 try {
156                     mService.ready();
157                 } catch (RemoteException e) {
158                 }
159             }
160         }
161 
162         @Override
163         public void onServiceDisconnected(ComponentName name) {
164             if (DEBUG) {
165                 Slog.d(TAG, "onServiceDisconnected to " + name);
166             }
167             synchronized (mServiceStub) {
168                 mService = null;
169                 resetHotwordDetectionConnectionLocked();
170             }
171         }
172 
173         @Override
174         public void onBindingDied(ComponentName name) {
175             Slog.d(TAG, "onBindingDied to " + name);
176             String packageName = name.getPackageName();
177             ParceledListSlice<ApplicationExitInfo> plistSlice = null;
178             try {
179                 plistSlice = mAm.getHistoricalProcessExitReasons(packageName, 0, 1, mUser);
180             } catch (RemoteException e) {
181                 // do nothing. The local binder so it can not throw it.
182             }
183             if (plistSlice == null) {
184                 return;
185             }
186             List<ApplicationExitInfo> list = plistSlice.getList();
187             if (list.isEmpty()) {
188                 return;
189             }
190             // TODO(b/229956310): Refactor the logic of PackageMonitor and onBindingDied
191             ApplicationExitInfo info = list.get(0);
192             if (info.getReason() == ApplicationExitInfo.REASON_USER_REQUESTED
193                     && info.getSubReason() == ApplicationExitInfo.SUBREASON_STOP_APP) {
194                 // only handle user stopped the application from the task manager
195                 mServiceStub.handleUserStop(packageName, mUser);
196             }
197         }
198     };
199 
VoiceInteractionManagerServiceImpl(Context context, Handler handler, VoiceInteractionManagerService.VoiceInteractionManagerServiceStub stub, int userHandle, ComponentName service)200     VoiceInteractionManagerServiceImpl(Context context, Handler handler,
201             VoiceInteractionManagerService.VoiceInteractionManagerServiceStub stub,
202             int userHandle, ComponentName service) {
203         mContext = context;
204         mHandler = handler;
205         mDirectActionsHandler = new Handler(true);
206         mServiceStub = stub;
207         mUser = userHandle;
208         mComponent = service;
209         mAm = ActivityManager.getService();
210         mAtm = ActivityTaskManager.getService();
211         mPackageManagerInternal = Objects.requireNonNull(
212                 LocalServices.getService(PackageManagerInternal.class));
213         VoiceInteractionServiceInfo info;
214         try {
215             info = new VoiceInteractionServiceInfo(context.getPackageManager(), service, mUser);
216         } catch (PackageManager.NameNotFoundException e) {
217             Slog.w(TAG, "Voice interaction service not found: " + service, e);
218             mInfo = null;
219             mSessionComponentName = null;
220             mHotwordDetectionComponentName = null;
221             mVisualQueryDetectionComponentName = null;
222             mIWindowManager = null;
223             mValid = false;
224             return;
225         }
226         mInfo = info;
227         if (mInfo.getParseError() != null) {
228             Slog.w(TAG, "Bad voice interaction service: " + mInfo.getParseError());
229             mSessionComponentName = null;
230             mHotwordDetectionComponentName = null;
231             mVisualQueryDetectionComponentName = null;
232             mIWindowManager = null;
233             mValid = false;
234             return;
235         }
236         mValid = true;
237         mSessionComponentName = new ComponentName(service.getPackageName(),
238                 mInfo.getSessionService());
239         final String hotwordDetectionServiceName = mInfo.getHotwordDetectionService();
240         mHotwordDetectionComponentName = hotwordDetectionServiceName != null
241                 ? new ComponentName(service.getPackageName(), hotwordDetectionServiceName) : null;
242         final String visualQueryDetectionServiceName = mInfo.getVisualQueryDetectionService();
243         mVisualQueryDetectionComponentName = visualQueryDetectionServiceName != null ? new
244                 ComponentName(service.getPackageName(), visualQueryDetectionServiceName) : null;
245         mIWindowManager = IWindowManager.Stub.asInterface(
246                 ServiceManager.getService(Context.WINDOW_SERVICE));
247         IntentFilter filter = new IntentFilter();
248         filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
249         mContext.registerReceiver(mBroadcastReceiver, filter, null, handler,
250                 Context.RECEIVER_EXPORTED);
251     }
252 
grantImplicitAccessLocked(int grantRecipientUid, @Nullable Intent intent)253     public void grantImplicitAccessLocked(int grantRecipientUid, @Nullable Intent intent) {
254         final int grantRecipientAppId = UserHandle.getAppId(grantRecipientUid);
255         final int grantRecipientUserId = UserHandle.getUserId(grantRecipientUid);
256         final int voiceInteractionUid = mInfo.getServiceInfo().applicationInfo.uid;
257         mPackageManagerInternal.grantImplicitAccess(
258                 grantRecipientUserId, intent, grantRecipientAppId, voiceInteractionUid,
259                 /* direct= */ true);
260     }
261 
showSessionLocked(@ullable Bundle args, int flags, @Nullable String attributionTag, @Nullable IVoiceInteractionSessionShowCallback showCallback, @Nullable IBinder activityToken)262     public boolean showSessionLocked(@Nullable Bundle args, int flags,
263             @Nullable String attributionTag,
264             @Nullable IVoiceInteractionSessionShowCallback showCallback,
265             @Nullable IBinder activityToken) {
266         final int sessionId = mServiceStub.getNextShowSessionId();
267         final Bundle newArgs = args == null ? new Bundle() : args;
268         newArgs.putInt(KEY_SHOW_SESSION_ID, sessionId);
269 
270         try {
271             if (mService != null) {
272                 mService.prepareToShowSession(newArgs, flags);
273             }
274         } catch (RemoteException e) {
275             Slog.w(TAG, "RemoteException while calling prepareToShowSession", e);
276         }
277 
278         if (mActiveSession == null) {
279             mActiveSession = new VoiceInteractionSessionConnection(mServiceStub,
280                     mSessionComponentName, mUser, mContext, this,
281                     mInfo.getServiceInfo().applicationInfo.uid, mHandler);
282         }
283         if (!mActiveSession.mBound) {
284             try {
285                 if (mService != null) {
286                     Bundle failedArgs = new Bundle();
287                     failedArgs.putInt(KEY_SHOW_SESSION_ID, sessionId);
288                     mService.showSessionFailed(failedArgs);
289                 }
290             } catch (RemoteException e) {
291                 Slog.w(TAG, "RemoteException while calling showSessionFailed", e);
292             }
293         }
294 
295         List<ActivityAssistInfo> allVisibleActivities =
296                 LocalServices.getService(ActivityTaskManagerInternal.class)
297                         .getTopVisibleActivities();
298 
299         List<ActivityAssistInfo> visibleActivities = null;
300         if (activityToken != null) {
301             visibleActivities = new ArrayList();
302             int activitiesCount = allVisibleActivities.size();
303             for (int i = 0; i < activitiesCount; i++) {
304                 ActivityAssistInfo info = allVisibleActivities.get(i);
305                 if (info.getActivityToken() == activityToken) {
306                     visibleActivities.add(info);
307                     break;
308                 }
309             }
310         } else {
311             visibleActivities = allVisibleActivities;
312         }
313         return mActiveSession.showLocked(newArgs, flags, attributionTag, mDisabledShowContext,
314                 showCallback, visibleActivities);
315     }
316 
getActiveServiceSupportedActions(List<String> commands, IVoiceActionCheckCallback callback)317     public void getActiveServiceSupportedActions(List<String> commands,
318             IVoiceActionCheckCallback callback) {
319         if (mService == null) {
320             Slog.w(TAG, "Not bound to voice interaction service " + mComponent);
321             try {
322                 callback.onComplete(null);
323             } catch (RemoteException e) {
324             }
325             return;
326         }
327         try {
328             mService.getActiveServiceSupportedActions(commands, callback);
329         } catch (RemoteException e) {
330             Slog.w(TAG, "RemoteException while calling getActiveServiceSupportedActions", e);
331         }
332     }
333 
hideSessionLocked()334     public boolean hideSessionLocked() {
335         if (mActiveSession != null) {
336             return mActiveSession.hideLocked();
337         }
338         return false;
339     }
340 
deliverNewSessionLocked(IBinder token, IVoiceInteractionSession session, IVoiceInteractor interactor)341     public boolean deliverNewSessionLocked(IBinder token,
342             IVoiceInteractionSession session, IVoiceInteractor interactor) {
343         if (mActiveSession == null || token != mActiveSession.mToken) {
344             Slog.w(TAG, "deliverNewSession does not match active session");
345             return false;
346         }
347         mActiveSession.deliverNewSessionLocked(session, interactor);
348         return true;
349     }
350 
startVoiceActivityLocked(@ullable String callingFeatureId, int callingPid, int callingUid, IBinder token, Intent intent, String resolvedType)351     public int startVoiceActivityLocked(@Nullable String callingFeatureId, int callingPid,
352             int callingUid, IBinder token, Intent intent, String resolvedType) {
353         try {
354             if (mActiveSession == null || token != mActiveSession.mToken) {
355                 Slog.w(TAG, "startVoiceActivity does not match active session");
356                 return START_VOICE_NOT_ACTIVE_SESSION;
357             }
358             if (!mActiveSession.mShown) {
359                 Slog.w(TAG, "startVoiceActivity not allowed on hidden session");
360                 return START_VOICE_HIDDEN_SESSION;
361             }
362             intent = new Intent(intent);
363             intent.addCategory(Intent.CATEGORY_VOICE);
364             intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
365             return mAtm.startVoiceActivity(mComponent.getPackageName(), callingFeatureId,
366                     callingPid, callingUid, intent, resolvedType, mActiveSession.mSession,
367                     mActiveSession.mInteractor, 0, null, null, mUser);
368         } catch (RemoteException e) {
369             throw new IllegalStateException("Unexpected remote error", e);
370         }
371     }
372 
startAssistantActivityLocked(@ullable String callingFeatureId, int callingPid, int callingUid, IBinder token, Intent intent, String resolvedType, @NonNull Bundle bundle)373     public int startAssistantActivityLocked(@Nullable String callingFeatureId, int callingPid,
374             int callingUid, IBinder token, Intent intent, String resolvedType,
375             @NonNull Bundle bundle) {
376         try {
377             if (mActiveSession == null || token != mActiveSession.mToken) {
378                 Slog.w(TAG, "startAssistantActivity does not match active session");
379                 return START_ASSISTANT_NOT_ACTIVE_SESSION;
380             }
381             if (!mActiveSession.mShown) {
382                 Slog.w(TAG, "startAssistantActivity not allowed on hidden session");
383                 return START_ASSISTANT_HIDDEN_SESSION;
384             }
385             intent = new Intent(intent);
386             intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
387             // TODO: make the key public hidden
388             bundle.putInt("android.activity.activityType", ACTIVITY_TYPE_ASSISTANT);
389             return mAtm.startAssistantActivity(mComponent.getPackageName(), callingFeatureId,
390                     callingPid, callingUid, intent, resolvedType, bundle, mUser);
391         } catch (RemoteException e) {
392             throw new IllegalStateException("Unexpected remote error", e);
393         }
394     }
395 
requestDirectActionsLocked(@onNull IBinder token, int taskId, @NonNull IBinder assistToken, @Nullable RemoteCallback cancellationCallback, @NonNull RemoteCallback callback)396     public void requestDirectActionsLocked(@NonNull IBinder token, int taskId,
397             @NonNull IBinder assistToken,  @Nullable RemoteCallback cancellationCallback,
398             @NonNull RemoteCallback callback) {
399         if (mActiveSession == null || token != mActiveSession.mToken) {
400             Slog.w(TAG, "requestDirectActionsLocked does not match active session");
401             callback.sendResult(null);
402             return;
403         }
404         final ActivityTokens tokens = LocalServices.getService(ActivityTaskManagerInternal.class)
405                 .getAttachedNonFinishingActivityForTask(taskId, null);
406         if (tokens == null || tokens.getAssistToken() != assistToken) {
407             Slog.w(TAG, "Unknown activity to query for direct actions");
408             mDirectActionsHandler.sendMessageDelayed(PooledLambda.obtainMessage(
409                     VoiceInteractionManagerServiceImpl::retryRequestDirectActions,
410                     VoiceInteractionManagerServiceImpl.this, token, taskId, assistToken,
411                     cancellationCallback, callback), REQUEST_DIRECT_ACTIONS_RETRY_TIME_MS);
412         } else {
413             grantImplicitAccessLocked(tokens.getUid(), /* intent= */ null);
414             try {
415                 tokens.getApplicationThread().requestDirectActions(tokens.getActivityToken(),
416                         mActiveSession.mInteractor, cancellationCallback, callback);
417             } catch (RemoteException e) {
418                 Slog.w("Unexpected remote error", e);
419                 callback.sendResult(null);
420             }
421         }
422     }
423 
retryRequestDirectActions(@onNull IBinder token, int taskId, @NonNull IBinder assistToken, @Nullable RemoteCallback cancellationCallback, @NonNull RemoteCallback callback)424     private void retryRequestDirectActions(@NonNull IBinder token, int taskId,
425             @NonNull IBinder assistToken,  @Nullable RemoteCallback cancellationCallback,
426             @NonNull RemoteCallback callback) {
427         synchronized (mServiceStub) {
428             if (mActiveSession == null || token != mActiveSession.mToken) {
429                 Slog.w(TAG, "retryRequestDirectActions does not match active session");
430                 callback.sendResult(null);
431                 return;
432             }
433             final ActivityTokens tokens = LocalServices.getService(
434                             ActivityTaskManagerInternal.class)
435                     .getAttachedNonFinishingActivityForTask(taskId, null);
436             if (tokens == null || tokens.getAssistToken() != assistToken) {
437                 Slog.w(TAG, "Unknown activity to query for direct actions during retrying");
438                 callback.sendResult(null);
439             } else {
440                 try {
441                     tokens.getApplicationThread().requestDirectActions(tokens.getActivityToken(),
442                             mActiveSession.mInteractor, cancellationCallback, callback);
443                 } catch (RemoteException e) {
444                     Slog.w("Unexpected remote error", e);
445                     callback.sendResult(null);
446                 }
447             }
448         }
449     }
450 
performDirectActionLocked(@onNull IBinder token, @NonNull String actionId, @Nullable Bundle arguments, int taskId, IBinder assistToken, @Nullable RemoteCallback cancellationCallback, @NonNull RemoteCallback resultCallback)451     void performDirectActionLocked(@NonNull IBinder token, @NonNull String actionId,
452             @Nullable Bundle arguments, int taskId, IBinder assistToken,
453             @Nullable RemoteCallback cancellationCallback,
454             @NonNull RemoteCallback resultCallback) {
455         if (mActiveSession == null || token != mActiveSession.mToken) {
456             Slog.w(TAG, "performDirectActionLocked does not match active session");
457             resultCallback.sendResult(null);
458             return;
459         }
460         final ActivityTokens tokens = LocalServices.getService(ActivityTaskManagerInternal.class)
461                 .getAttachedNonFinishingActivityForTask(taskId, null);
462         if (tokens == null || tokens.getAssistToken() != assistToken) {
463             Slog.w(TAG, "Unknown activity to perform a direct action");
464             resultCallback.sendResult(null);
465         } else {
466             try {
467                 tokens.getApplicationThread().performDirectAction(tokens.getActivityToken(),
468                         actionId, arguments, cancellationCallback,
469                         resultCallback);
470             } catch (RemoteException e) {
471                 Slog.w("Unexpected remote error", e);
472                 resultCallback.sendResult(null);
473             }
474         }
475     }
476 
setKeepAwakeLocked(IBinder token, boolean keepAwake)477     public void setKeepAwakeLocked(IBinder token, boolean keepAwake) {
478         try {
479             if (mActiveSession == null || token != mActiveSession.mToken) {
480                 Slog.w(TAG, "setKeepAwake does not match active session");
481                 return;
482             }
483             mAtm.setVoiceKeepAwake(mActiveSession.mSession, keepAwake);
484         } catch (RemoteException e) {
485             throw new IllegalStateException("Unexpected remote error", e);
486         }
487     }
488 
closeSystemDialogsLocked(IBinder token)489     public void closeSystemDialogsLocked(IBinder token) {
490         try {
491             if (mActiveSession == null || token != mActiveSession.mToken) {
492                 Slog.w(TAG, "closeSystemDialogs does not match active session");
493                 return;
494             }
495             mAm.closeSystemDialogs(CLOSE_REASON_VOICE_INTERACTION);
496         } catch (RemoteException e) {
497             throw new IllegalStateException("Unexpected remote error", e);
498         }
499     }
500 
finishLocked(IBinder token, boolean finishTask)501     public void finishLocked(IBinder token, boolean finishTask) {
502         if (mActiveSession == null || (!finishTask && token != mActiveSession.mToken)) {
503             Slog.w(TAG, "finish does not match active session");
504             return;
505         }
506         mActiveSession.cancelLocked(finishTask);
507         mActiveSession = null;
508     }
509 
setDisabledShowContextLocked(int callingUid, int flags)510     public void setDisabledShowContextLocked(int callingUid, int flags) {
511         int activeUid = mInfo.getServiceInfo().applicationInfo.uid;
512         if (callingUid != activeUid) {
513             throw new SecurityException("Calling uid " + callingUid
514                     + " does not match active uid " + activeUid);
515         }
516         mDisabledShowContext = flags;
517     }
518 
getDisabledShowContextLocked(int callingUid)519     public int getDisabledShowContextLocked(int callingUid) {
520         int activeUid = mInfo.getServiceInfo().applicationInfo.uid;
521         if (callingUid != activeUid) {
522             throw new SecurityException("Calling uid " + callingUid
523                     + " does not match active uid " + activeUid);
524         }
525         return mDisabledShowContext;
526     }
527 
getUserDisabledShowContextLocked(int callingUid)528     public int getUserDisabledShowContextLocked(int callingUid) {
529         int activeUid = mInfo.getServiceInfo().applicationInfo.uid;
530         if (callingUid != activeUid) {
531             throw new SecurityException("Calling uid " + callingUid
532                     + " does not match active uid " + activeUid);
533         }
534         return mActiveSession != null ? mActiveSession.getUserDisabledShowContextLocked() : 0;
535     }
536 
supportsLocalVoiceInteraction()537     public boolean supportsLocalVoiceInteraction() {
538         return mInfo.getSupportsLocalInteraction();
539     }
540 
startListeningVisibleActivityChangedLocked(@onNull IBinder token)541     public void startListeningVisibleActivityChangedLocked(@NonNull IBinder token) {
542         if (DEBUG) {
543             Slog.d(TAG, "startListeningVisibleActivityChangedLocked: token=" + token);
544         }
545         if (mActiveSession == null || token != mActiveSession.mToken) {
546             Slog.w(TAG, "startListeningVisibleActivityChangedLocked does not match"
547                     + " active session");
548             return;
549         }
550         mActiveSession.startListeningVisibleActivityChangedLocked();
551     }
552 
stopListeningVisibleActivityChangedLocked(@onNull IBinder token)553     public void stopListeningVisibleActivityChangedLocked(@NonNull IBinder token) {
554         if (DEBUG) {
555             Slog.d(TAG, "stopListeningVisibleActivityChangedLocked: token=" + token);
556         }
557         if (mActiveSession == null || token != mActiveSession.mToken) {
558             Slog.w(TAG, "stopListeningVisibleActivityChangedLocked does not match"
559                     + " active session");
560             return;
561         }
562         mActiveSession.stopListeningVisibleActivityChangedLocked();
563     }
564 
notifyActivityDestroyedLocked(@onNull IBinder activityToken)565     public void notifyActivityDestroyedLocked(@NonNull IBinder activityToken) {
566         if (DEBUG) {
567             Slog.d(TAG, "notifyActivityDestroyedLocked activityToken=" + activityToken);
568         }
569         if (mActiveSession == null || !mActiveSession.mShown) {
570             if (DEBUG) {
571                 Slog.d(TAG, "notifyActivityDestroyedLocked not allowed on no session or"
572                         + " hidden session");
573             }
574             return;
575         }
576         mActiveSession.notifyActivityDestroyedLocked(activityToken);
577     }
578 
notifyActivityEventChangedLocked(@onNull IBinder activityToken, int type)579     public void notifyActivityEventChangedLocked(@NonNull IBinder activityToken, int type) {
580         if (DEBUG) {
581             Slog.d(TAG, "notifyActivityEventChangedLocked type=" + type);
582         }
583         if (mActiveSession == null || !mActiveSession.mShown) {
584             if (DEBUG) {
585                 Slog.d(TAG, "notifyActivityEventChangedLocked not allowed on no session or"
586                         + " hidden session");
587             }
588             return;
589         }
590         mActiveSession.notifyActivityEventChangedLocked(activityToken, type);
591     }
592 
updateStateLocked( @ullable PersistableBundle options, @Nullable SharedMemory sharedMemory, @NonNull IBinder token)593     public void updateStateLocked(
594             @Nullable PersistableBundle options,
595             @Nullable SharedMemory sharedMemory,
596             @NonNull IBinder token) {
597         Slog.v(TAG, "updateStateLocked");
598 
599         if (sharedMemory != null && !sharedMemory.setProtect(OsConstants.PROT_READ)) {
600             Slog.w(TAG, "Can't set sharedMemory to be read-only");
601             throw new IllegalStateException("Can't set sharedMemory to be read-only");
602         }
603 
604         if (mHotwordDetectionConnection == null) {
605             Slog.w(TAG, "update State, but no hotword detection connection");
606             throw new IllegalStateException("Hotword detection connection not found");
607         }
608         synchronized (mHotwordDetectionConnection.mLock) {
609             mHotwordDetectionConnection.updateStateLocked(options, sharedMemory, token);
610         }
611     }
612 
verifyDetectorForHotwordDetectionLocked( @ullable SharedMemory sharedMemory, IHotwordRecognitionStatusCallback callback, int detectorType)613     private void verifyDetectorForHotwordDetectionLocked(
614             @Nullable SharedMemory sharedMemory,
615             IHotwordRecognitionStatusCallback callback,
616             int detectorType) {
617         Slog.v(TAG, "verifyDetectorForHotwordDetectionLocked");
618         int voiceInteractionServiceUid = mInfo.getServiceInfo().applicationInfo.uid;
619         if (mHotwordDetectionComponentName == null) {
620             Slog.w(TAG, "Hotword detection service name not found");
621             logDetectorCreateEventIfNeeded(callback, detectorType, false,
622                     voiceInteractionServiceUid);
623             throw new IllegalStateException("Hotword detection service name not found");
624         }
625         ServiceInfo hotwordDetectionServiceInfo = getServiceInfoLocked(
626                 mHotwordDetectionComponentName, mUser);
627         if (hotwordDetectionServiceInfo == null) {
628             Slog.w(TAG, "Hotword detection service info not found");
629             logDetectorCreateEventIfNeeded(callback, detectorType, false,
630                     voiceInteractionServiceUid);
631             throw new IllegalStateException("Hotword detection service info not found");
632         }
633         if (!isIsolatedProcessLocked(hotwordDetectionServiceInfo)) {
634             Slog.w(TAG, "Hotword detection service not in isolated process");
635             logDetectorCreateEventIfNeeded(callback, detectorType, false,
636                     voiceInteractionServiceUid);
637             throw new IllegalStateException("Hotword detection service not in isolated process");
638         }
639         if (!Manifest.permission.BIND_HOTWORD_DETECTION_SERVICE.equals(
640                 hotwordDetectionServiceInfo.permission)) {
641             Slog.w(TAG, "Hotword detection service does not require permission "
642                     + Manifest.permission.BIND_HOTWORD_DETECTION_SERVICE);
643             logDetectorCreateEventIfNeeded(callback, detectorType, false,
644                     voiceInteractionServiceUid);
645             throw new SecurityException("Hotword detection service does not require permission "
646                     + Manifest.permission.BIND_HOTWORD_DETECTION_SERVICE);
647         }
648         if (mContext.getPackageManager().checkPermission(
649                 Manifest.permission.BIND_HOTWORD_DETECTION_SERVICE,
650                 mInfo.getServiceInfo().packageName) == PackageManager.PERMISSION_GRANTED) {
651             Slog.w(TAG, "Voice interaction service should not hold permission "
652                     + Manifest.permission.BIND_HOTWORD_DETECTION_SERVICE);
653             logDetectorCreateEventIfNeeded(callback, detectorType, false,
654                     voiceInteractionServiceUid);
655             throw new SecurityException("Voice interaction service should not hold permission "
656                     + Manifest.permission.BIND_HOTWORD_DETECTION_SERVICE);
657         }
658 
659         if (sharedMemory != null && !sharedMemory.setProtect(OsConstants.PROT_READ)) {
660             Slog.w(TAG, "Can't set sharedMemory to be read-only");
661             logDetectorCreateEventIfNeeded(callback, detectorType, false,
662                     voiceInteractionServiceUid);
663             throw new IllegalStateException("Can't set sharedMemory to be read-only");
664         }
665 
666         logDetectorCreateEventIfNeeded(callback, detectorType, true,
667                 voiceInteractionServiceUid);
668     }
669 
verifyDetectorForVisualQueryDetectionLocked(@ullable SharedMemory sharedMemory)670     private void verifyDetectorForVisualQueryDetectionLocked(@Nullable SharedMemory sharedMemory) {
671         Slog.v(TAG, "verifyDetectorForVisualQueryDetectionLocked");
672 
673         if (mVisualQueryDetectionComponentName == null) {
674             Slog.w(TAG, "Visual query detection service name not found");
675             throw new IllegalStateException("Visual query detection service name not found");
676         }
677         ServiceInfo visualQueryDetectionServiceInfo = getServiceInfoLocked(
678                 mVisualQueryDetectionComponentName, mUser);
679         if (visualQueryDetectionServiceInfo == null) {
680             Slog.w(TAG, "Visual query detection service info not found");
681             throw new IllegalStateException("Visual query detection service name not found");
682         }
683         if (!isIsolatedProcessLocked(visualQueryDetectionServiceInfo)) {
684             Slog.w(TAG, "Visual query detection service not in isolated process");
685             throw new IllegalStateException("Visual query detection not in isolated process");
686         }
687         if (!Manifest.permission.BIND_VISUAL_QUERY_DETECTION_SERVICE.equals(
688                 visualQueryDetectionServiceInfo.permission)) {
689             Slog.w(TAG, "Visual query detection does not require permission "
690                     + Manifest.permission.BIND_VISUAL_QUERY_DETECTION_SERVICE);
691             throw new SecurityException("Visual query detection does not require permission "
692                     + Manifest.permission.BIND_VISUAL_QUERY_DETECTION_SERVICE);
693         }
694         if (mContext.getPackageManager().checkPermission(
695                 Manifest.permission.BIND_VISUAL_QUERY_DETECTION_SERVICE,
696                 mInfo.getServiceInfo().packageName) == PackageManager.PERMISSION_GRANTED) {
697             Slog.w(TAG, "Voice interaction service should not hold permission "
698                     + Manifest.permission.BIND_VISUAL_QUERY_DETECTION_SERVICE);
699             throw new SecurityException("Voice interaction service should not hold permission "
700                     + Manifest.permission.BIND_VISUAL_QUERY_DETECTION_SERVICE);
701         }
702         if (sharedMemory != null && !sharedMemory.setProtect(OsConstants.PROT_READ)) {
703             Slog.w(TAG, "Can't set sharedMemory to be read-only");
704             throw new IllegalStateException("Can't set sharedMemory to be read-only");
705         }
706     }
707 
initAndVerifyDetectorLocked( @onNull Identity voiceInteractorIdentity, @Nullable PersistableBundle options, @Nullable SharedMemory sharedMemory, @NonNull IBinder token, IHotwordRecognitionStatusCallback callback, int detectorType)708     public void initAndVerifyDetectorLocked(
709             @NonNull Identity voiceInteractorIdentity,
710             @Nullable PersistableBundle options,
711             @Nullable SharedMemory sharedMemory,
712             @NonNull IBinder token,
713             IHotwordRecognitionStatusCallback callback,
714             int detectorType) {
715 
716         if (detectorType != HotwordDetector.DETECTOR_TYPE_VISUAL_QUERY_DETECTOR) {
717             verifyDetectorForHotwordDetectionLocked(sharedMemory, callback, detectorType);
718         } else {
719             verifyDetectorForVisualQueryDetectionLocked(sharedMemory);
720         }
721         if (SYSPROP_VISUAL_QUERY_SERVICE_ENABLED && !verifyProcessSharingLocked()) {
722             Slog.w(TAG, "Sandboxed detection service not in shared isolated process");
723             throw new IllegalStateException("VisualQueryDetectionService or HotworDetectionService "
724                     + "not in a shared isolated process. Please make sure to set "
725                     + "android:allowSharedIsolatedProcess and android:isolatedProcess to be true "
726                     + "and android:externalService to be false in the manifest file");
727         }
728 
729         if (mHotwordDetectionConnection == null) {
730             mHotwordDetectionConnection = new HotwordDetectionConnection(mServiceStub, mContext,
731                     mInfo.getServiceInfo().applicationInfo.uid, voiceInteractorIdentity,
732                     mHotwordDetectionComponentName, mVisualQueryDetectionComponentName, mUser,
733                     /* bindInstantServiceAllowed= */ false, detectorType,
734                     (token1, detectorType1) -> {
735                         try {
736                             mService.detectorRemoteExceptionOccurred(token1, detectorType1);
737                         } catch (RemoteException e) {
738                             Slog.w(TAG, "Fail to notify client detector remote "
739                                     + "exception occurred.");
740                         }
741                     });
742         } else if (detectorType != HotwordDetector.DETECTOR_TYPE_VISUAL_QUERY_DETECTOR) {
743             // TODO: Logger events should be handled in session instead. Temporary adding the
744             //  checking to prevent confusion so VisualQueryDetection events won't be logged if the
745             //  connection is instantiated by the VisualQueryDetector.
746             mHotwordDetectionConnection.setDetectorType(detectorType);
747         }
748         mHotwordDetectionConnection.createDetectorLocked(options, sharedMemory, token, callback,
749                 detectorType);
750     }
751 
destroyDetectorLocked(IBinder token)752     public void destroyDetectorLocked(IBinder token) {
753         Slog.v(TAG, "destroyDetectorLocked");
754 
755         if (mHotwordDetectionConnection == null) {
756             Slog.w(TAG, "destroy detector callback, but no hotword detection connection");
757             return;
758         }
759         mHotwordDetectionConnection.destroyDetectorLocked(token);
760     }
761 
logDetectorCreateEventIfNeeded(IHotwordRecognitionStatusCallback callback, int detectorType, boolean isCreated, int voiceInteractionServiceUid)762     private void logDetectorCreateEventIfNeeded(IHotwordRecognitionStatusCallback callback,
763             int detectorType, boolean isCreated, int voiceInteractionServiceUid) {
764         if (callback != null) {
765             HotwordMetricsLogger.writeDetectorCreateEvent(detectorType, isCreated,
766                     voiceInteractionServiceUid);
767         }
768     }
769 
shutdownHotwordDetectionServiceLocked()770     public void shutdownHotwordDetectionServiceLocked() {
771         if (DEBUG) {
772             Slog.d(TAG, "shutdownHotwordDetectionServiceLocked");
773         }
774         if (mHotwordDetectionConnection == null) {
775             Slog.w(TAG, "shutdown, but no hotword detection connection");
776             return;
777         }
778         mHotwordDetectionConnection.cancelLocked();
779         mHotwordDetectionConnection = null;
780     }
781 
setVisualQueryDetectionAttentionListenerLocked( @ullable IVisualQueryDetectionAttentionListener listener)782     public void setVisualQueryDetectionAttentionListenerLocked(
783             @Nullable IVisualQueryDetectionAttentionListener listener) {
784         if (mHotwordDetectionConnection == null) {
785             return;
786         }
787         mHotwordDetectionConnection.setVisualQueryDetectionAttentionListenerLocked(listener);
788     }
789 
startPerceivingLocked(IVisualQueryDetectionVoiceInteractionCallback callback)790     public boolean startPerceivingLocked(IVisualQueryDetectionVoiceInteractionCallback callback) {
791         if (DEBUG) {
792             Slog.d(TAG, "startPerceivingLocked");
793         }
794 
795         if (mHotwordDetectionConnection == null) {
796             // TODO: callback.onError();
797             return false;
798         }
799 
800         return mHotwordDetectionConnection.startPerceivingLocked(callback);
801     }
802 
stopPerceivingLocked()803     public boolean stopPerceivingLocked() {
804         if (DEBUG) {
805             Slog.d(TAG, "stopPerceivingLocked");
806         }
807 
808         if (mHotwordDetectionConnection == null) {
809             Slog.w(TAG, "stopPerceivingLocked() called but connection isn't established");
810             return false;
811         }
812 
813         return mHotwordDetectionConnection.stopPerceivingLocked();
814     }
815 
startListeningFromMicLocked( AudioFormat audioFormat, IMicrophoneHotwordDetectionVoiceInteractionCallback callback)816     public void startListeningFromMicLocked(
817             AudioFormat audioFormat,
818             IMicrophoneHotwordDetectionVoiceInteractionCallback callback) {
819         if (DEBUG) {
820             Slog.d(TAG, "startListeningFromMicLocked");
821         }
822 
823         if (mHotwordDetectionConnection == null) {
824             // TODO: callback.onError();
825             return;
826         }
827 
828         mHotwordDetectionConnection.startListeningFromMicLocked(audioFormat, callback);
829     }
830 
startListeningFromExternalSourceLocked( ParcelFileDescriptor audioStream, AudioFormat audioFormat, @Nullable PersistableBundle options, @NonNull IBinder token, IMicrophoneHotwordDetectionVoiceInteractionCallback callback)831     public void startListeningFromExternalSourceLocked(
832             ParcelFileDescriptor audioStream,
833             AudioFormat audioFormat,
834             @Nullable PersistableBundle options,
835             @NonNull IBinder token,
836             IMicrophoneHotwordDetectionVoiceInteractionCallback callback) {
837         if (DEBUG) {
838             Slog.d(TAG, "startListeningFromExternalSourceLocked");
839         }
840 
841         if (mHotwordDetectionConnection == null) {
842             // TODO: callback.onError();
843             return;
844         }
845 
846         if (audioStream == null) {
847             Slog.w(TAG, "External source is null for hotword detector");
848             throw new IllegalStateException("External source is null for hotword detector");
849         }
850 
851         mHotwordDetectionConnection.startListeningFromExternalSourceLocked(audioStream, audioFormat,
852                 options, token, callback);
853     }
854 
stopListeningFromMicLocked()855     public void stopListeningFromMicLocked() {
856         if (DEBUG) {
857             Slog.d(TAG, "stopListeningFromMicLocked");
858         }
859 
860         if (mHotwordDetectionConnection == null) {
861             Slog.w(TAG, "stopListeningFromMicLocked() called but connection isn't established");
862             return;
863         }
864 
865         mHotwordDetectionConnection.stopListeningFromMicLocked();
866     }
867 
triggerHardwareRecognitionEventForTestLocked( SoundTrigger.KeyphraseRecognitionEvent event, IHotwordRecognitionStatusCallback callback)868     public void triggerHardwareRecognitionEventForTestLocked(
869             SoundTrigger.KeyphraseRecognitionEvent event,
870             IHotwordRecognitionStatusCallback callback) {
871         if (DEBUG) {
872             Slog.d(TAG, "triggerHardwareRecognitionEventForTestLocked");
873         }
874         if (mHotwordDetectionConnection == null) {
875             Slog.w(TAG, "triggerHardwareRecognitionEventForTestLocked() called but connection"
876                     + " isn't established");
877             return;
878         }
879         mHotwordDetectionConnection.triggerHardwareRecognitionEventForTestLocked(event, callback);
880     }
881 
createSoundTriggerCallbackLocked( Context context, IHotwordRecognitionStatusCallback callback, Identity voiceInteractorIdentity)882     public IRecognitionStatusCallback createSoundTriggerCallbackLocked(
883             Context context, IHotwordRecognitionStatusCallback callback,
884             Identity voiceInteractorIdentity) {
885         if (DEBUG) {
886             Slog.d(TAG, "createSoundTriggerCallbackLocked");
887         }
888         return new HotwordDetectionConnection.SoundTriggerCallback(context, callback,
889                 mHotwordDetectionConnection, voiceInteractorIdentity);
890     }
891 
getServiceInfoLocked(@onNull ComponentName componentName, int userHandle)892     private static ServiceInfo getServiceInfoLocked(@NonNull ComponentName componentName,
893             int userHandle) {
894         try {
895             return AppGlobals.getPackageManager().getServiceInfo(componentName,
896                     PackageManager.GET_META_DATA
897                             | PackageManager.MATCH_DIRECT_BOOT_AWARE
898                             | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userHandle);
899         } catch (RemoteException e) {
900             if (DEBUG) {
901                 Slog.w(TAG, "getServiceInfoLocked RemoteException : " + e);
902             }
903         }
904         return null;
905     }
906 
isIsolatedProcessLocked(@onNull ServiceInfo serviceInfo)907     boolean isIsolatedProcessLocked(@NonNull ServiceInfo serviceInfo) {
908         return (serviceInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) != 0
909                 && (serviceInfo.flags & ServiceInfo.FLAG_EXTERNAL_SERVICE) == 0;
910     }
911 
verifyProcessSharingLocked()912     boolean verifyProcessSharingLocked() {
913         // only check this if both VQDS and HDS are declared in the app
914         ServiceInfo hotwordInfo = getServiceInfoLocked(mHotwordDetectionComponentName, mUser);
915         ServiceInfo visualQueryInfo =
916                 getServiceInfoLocked(mVisualQueryDetectionComponentName, mUser);
917         if (hotwordInfo == null || visualQueryInfo == null) {
918             return true;
919         }
920         // Enforce shared isolated option is used when VisualQueryDetectionservice is enabled
921         return (hotwordInfo.flags & ServiceInfo.FLAG_ALLOW_SHARED_ISOLATED_PROCESS) != 0
922                 && (visualQueryInfo.flags & ServiceInfo.FLAG_ALLOW_SHARED_ISOLATED_PROCESS) != 0;
923     }
924 
925 
forceRestartHotwordDetector()926     void forceRestartHotwordDetector() {
927         if (mHotwordDetectionConnection == null) {
928             Slog.w(TAG, "Failed to force-restart hotword detection: no hotword detection active");
929             return;
930         }
931         mHotwordDetectionConnection.forceRestart();
932     }
933 
setDebugHotwordLoggingLocked(boolean logging)934     void setDebugHotwordLoggingLocked(boolean logging) {
935         if (mHotwordDetectionConnection == null) {
936             Slog.w(TAG, "Failed to set temporary debug logging: no hotword detection active");
937             return;
938         }
939         mHotwordDetectionConnection.setDebugHotwordLoggingLocked(logging);
940     }
941 
resetHotwordDetectionConnectionLocked()942     void resetHotwordDetectionConnectionLocked() {
943         if (DEBUG) {
944             Slog.d(TAG, "resetHotwordDetectionConnectionLocked");
945         }
946         if (mHotwordDetectionConnection == null) {
947             if (DEBUG) {
948                 Slog.w(TAG, "reset, but no hotword detection connection");
949             }
950             return;
951         }
952         mHotwordDetectionConnection.cancelLocked();
953         mHotwordDetectionConnection = null;
954     }
955 
dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args)956     public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args) {
957         if (!mValid) {
958             pw.print("  NOT VALID: ");
959             if (mInfo == null) {
960                 pw.println("no info");
961             } else {
962                 pw.println(mInfo.getParseError());
963             }
964             return;
965         }
966         pw.print("  mUser="); pw.println(mUser);
967         pw.print("  mComponent="); pw.println(mComponent.flattenToShortString());
968         pw.print("  Session service="); pw.println(mInfo.getSessionService());
969         pw.println("  Service info:");
970         mInfo.getServiceInfo().dump(new PrintWriterPrinter(pw), "    ");
971         pw.print("  Recognition service="); pw.println(mInfo.getRecognitionService());
972         pw.print("  Hotword detection service="); pw.println(mInfo.getHotwordDetectionService());
973         pw.print("  Settings activity="); pw.println(mInfo.getSettingsActivity());
974         pw.print("  Supports assist="); pw.println(mInfo.getSupportsAssist());
975         pw.print("  Supports launch from keyguard=");
976         pw.println(mInfo.getSupportsLaunchFromKeyguard());
977         if (mDisabledShowContext != 0) {
978             pw.print("  mDisabledShowContext=");
979             pw.println(Integer.toHexString(mDisabledShowContext));
980         }
981         pw.print("  mBound="); pw.print(mBound);  pw.print(" mService="); pw.println(mService);
982         if (mHotwordDetectionConnection != null) {
983             pw.println("  Hotword detection connection:");
984             mHotwordDetectionConnection.dump("    ", pw);
985         } else {
986             pw.println("  No Hotword detection connection");
987         }
988         if (mActiveSession != null) {
989             pw.println("  Active session:");
990             mActiveSession.dump("    ", pw);
991         }
992     }
993 
startLocked()994     void startLocked() {
995         Intent intent = new Intent(VoiceInteractionService.SERVICE_INTERFACE);
996         intent.setComponent(mComponent);
997         mBound = mContext.bindServiceAsUser(intent, mConnection,
998                 Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE
999                 | Context.BIND_INCLUDE_CAPABILITIES
1000                 | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS, new UserHandle(mUser));
1001         if (!mBound) {
1002             Slog.w(TAG, "Failed binding to voice interaction service " + mComponent);
1003         }
1004     }
1005 
launchVoiceAssistFromKeyguard()1006     public void launchVoiceAssistFromKeyguard() {
1007         if (mService == null) {
1008             Slog.w(TAG, "Not bound to voice interaction service " + mComponent);
1009             return;
1010         }
1011         try {
1012             mService.launchVoiceAssistFromKeyguard();
1013         } catch (RemoteException e) {
1014             Slog.w(TAG, "RemoteException while calling launchVoiceAssistFromKeyguard", e);
1015         }
1016     }
1017 
shutdownLocked()1018     void shutdownLocked() {
1019         // If there is an active session, cancel it to allow it to clean up its window and other
1020         // state.
1021         if (mActiveSession != null) {
1022             mActiveSession.cancelLocked(false);
1023             mActiveSession = null;
1024         }
1025         try {
1026             if (mService != null) {
1027                 mService.shutdown();
1028             }
1029         } catch (RemoteException e) {
1030             Slog.w(TAG, "RemoteException in shutdown", e);
1031         }
1032         if (mHotwordDetectionConnection != null) {
1033             mHotwordDetectionConnection.cancelLocked();
1034             mHotwordDetectionConnection = null;
1035         }
1036         if (mBound) {
1037             mContext.unbindService(mConnection);
1038             mBound = false;
1039         }
1040         if (mValid) {
1041             mContext.unregisterReceiver(mBroadcastReceiver);
1042         }
1043     }
1044 
notifySoundModelsChangedLocked()1045     void notifySoundModelsChangedLocked() {
1046         if (mService == null) {
1047             Slog.w(TAG, "Not bound to voice interaction service " + mComponent);
1048             return;
1049         }
1050         try {
1051             mService.soundModelsChanged();
1052         } catch (RemoteException e) {
1053             Slog.w(TAG, "RemoteException while calling soundModelsChanged", e);
1054         }
1055     }
1056 
1057     @Override
sessionConnectionGone(VoiceInteractionSessionConnection connection)1058     public void sessionConnectionGone(VoiceInteractionSessionConnection connection) {
1059         synchronized (mServiceStub) {
1060             finishLocked(connection.mToken, false);
1061         }
1062     }
1063 
1064     @Override
onSessionShown(VoiceInteractionSessionConnection connection)1065     public void onSessionShown(VoiceInteractionSessionConnection connection) {
1066         mServiceStub.onSessionShown();
1067     }
1068 
1069     @Override
onSessionHidden(VoiceInteractionSessionConnection connection)1070     public void onSessionHidden(VoiceInteractionSessionConnection connection) {
1071         mServiceStub.onSessionHidden();
1072         // Notifies visibility change here can cause duplicate events, it is added to make sure
1073         // client always get the callback even if session is unexpectedly closed.
1074         mServiceStub.setSessionWindowVisible(connection.mToken, false);
1075     }
1076 
1077     interface DetectorRemoteExceptionListener {
onDetectorRemoteException(@onNull IBinder token, int detectorType)1078         void onDetectorRemoteException(@NonNull IBinder token, int detectorType);
1079     }
1080 }
1081