1 /*
2  * Copyright (C) 2022 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 android.voiceinteraction.cts.services;
18 
19 import static android.voiceinteraction.cts.testcore.Helper.KEYPHRASE_LOCALE;
20 import static android.voiceinteraction.cts.testcore.Helper.KEYPHRASE_TEXT;
21 import static android.voiceinteraction.cts.testcore.Helper.WAIT_TIMEOUT_IN_MS;
22 
23 import android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel;
24 import android.media.voice.KeyphraseModelManager;
25 import android.os.Bundle;
26 import android.os.Looper;
27 import android.os.PersistableBundle;
28 import android.service.voice.AlwaysOnHotwordDetector;
29 import android.service.voice.HotwordDetector;
30 import android.service.voice.HotwordRejectedResult;
31 import android.service.voice.VisualQueryDetectedResult;
32 import android.service.voice.VisualQueryDetectionServiceFailure;
33 import android.service.voice.VisualQueryDetector;
34 import android.service.voice.VoiceInteractionService;
35 import android.util.Log;
36 import android.voiceinteraction.common.Utils;
37 import android.voiceinteraction.cts.testcore.Helper;
38 
39 import androidx.annotation.NonNull;
40 import androidx.annotation.Nullable;
41 
42 import java.io.File;
43 import java.io.FileOutputStream;
44 import java.util.concurrent.CountDownLatch;
45 import java.util.concurrent.Executor;
46 import java.util.concurrent.Executors;
47 import java.util.concurrent.TimeUnit;
48 
49 /**
50  * The base {@link VoiceInteractionService} that provides the common methods for subclass.
51  */
52 public abstract class BaseVoiceInteractionService extends VoiceInteractionService {
53 
54     private final String mTag = getClass().getSimpleName();
55     public static final int STATUS_NO_CALLBACK_CALLED = -100;
56 
57     // The service instance
58     public static VoiceInteractionService sService;
59     // The CountDownLatch waits for service connect
60     public static CountDownLatch sConnectLatch;
61     public static CountDownLatch sDisconnectLatch;
62 
63     // The CountDownLatch waits for onPrepareToShowSession
64     public static CountDownLatch sPrepareToShowSessionLatch;
65     // The CountDownLatch waits for onShowSessionFailed
66     public static CountDownLatch sShowSessionFailedLatch;
67 
68     // The CountDownLatch waits for a service init result
69     // TODO: rename to mHotwordDetectionServiceInitializedLatch, keep this name until the
70     //  refactor done. The original tests use trigger in many places. To make the mapping asier,
71     //  keep the current name now.
72     public CountDownLatch mDetectorInitializedLatch = null;
73 
74     VisualQueryDetector mVisualQueryDetector = null;
75     HotwordDetector mSoftwareHotwordDetector = null;
76     // The AlwaysOnHotwordDetector created by createAlwaysOnHotwordDetector() API
77     AlwaysOnHotwordDetector mAlwaysOnHotwordDetector = null;
78     // Throws IllegalStateException when calling createAlwaysOnHotwordDetector() API
79     private boolean mIsCreateDetectorIllegalStateExceptionThrow = false;
80     // Whether the callback of the detector is running on main thread or not
81     private boolean mIsDetectorCallbackRunningOnMainThread = false;
82     // Throws SecurityException when calling createAlwaysOnHotwordDetector() API
83     private boolean mIsCreateDetectorSecurityExceptionThrow = false;
84     private Bundle mPrepareToShowSessionArgs = new Bundle();
85     private Bundle mShowSessionFailedArgs = new Bundle();
86     private int mPrepareToShowSessionFlags = -1;
87     // the status of onHotwordDetectionServiceInitialized()
88     int mInitializedStatus = STATUS_NO_CALLBACK_CALLED;
89 
90     int mAvailabilityStatus = STATUS_NO_CALLBACK_CALLED;
91 
92 
93     final HotwordDetector.Callback mNoOpSoftwareDetectorCallback = new HotwordDetector.Callback() {
94         @Override
95         public void onDetected(AlwaysOnHotwordDetector.EventPayload eventPayload) {
96             // no-op
97         }
98 
99         @Override
100         public void onError() {
101             // no-op
102         }
103 
104         @Override
105         public void onRecognitionPaused() {
106             // no-op
107         }
108 
109         @Override
110         public void onRecognitionResumed() {
111             // no-op
112         }
113 
114         @Override
115         public void onRejected(HotwordRejectedResult result) {
116             // no-op
117         }
118 
119         @Override
120         public void onHotwordDetectionServiceInitialized(int status) {
121             // no-op
122         }
123 
124         @Override
125         public void onHotwordDetectionServiceRestarted() {
126             // no-op
127         }
128     };
129 
130     // An AlwaysOnHotwordDetector.Callback no nothing on callback methods
131     final AlwaysOnHotwordDetector.Callback mNoOpHotwordDetectorCallback =
132             new AlwaysOnHotwordDetector.Callback() {
133                 @Override
134                 public void onAvailabilityChanged(int status) {
135                     // no-op
136                 }
137 
138                 @Override
139                 public void onDetected(AlwaysOnHotwordDetector.EventPayload eventPayload) {
140                     // no-op
141                 }
142 
143                 @Override
144                 public void onRejected(@NonNull HotwordRejectedResult result) {
145                     // no-op
146                 }
147 
148                 @Override
149                 public void onError() {
150                     // no-op
151                 }
152 
153                 @Override
154                 public void onRecognitionPaused() {
155                     // no-op
156                 }
157 
158                 @Override
159                 public void onRecognitionResumed() {
160                     // no-op
161                 }
162 
163                 @Override
164                 public void onHotwordDetectionServiceInitialized(int status) {
165                     Log.i(mTag, "onHotwordDetectionServiceInitialized status = " + status);
166                 }
167 
168                 @Override
169                 public void onHotwordDetectionServiceRestarted() {
170                     // no-op
171                 }
172             };
173 
174     // A VisualQueryDetector.Callback no nothing on callback methods
175     final VisualQueryDetector.Callback mNoOpVisualQueryDetectorCallback =
176             new VisualQueryDetector.Callback() {
177                 @Override
178                 public void onQueryDetected(@NonNull String partialQuery) {
179                     //No-op
180                 }
181 
182                 @Override
183                 public void onQueryDetected(@NonNull VisualQueryDetectedResult partialResult) {
184                     //No-op
185                 }
186 
187                 @Override
188                 public void onQueryRejected() {
189                     //No-op
190                 }
191 
192                 @Override
193                 public void onQueryFinished() {
194                     //No-op
195                 }
196 
197                 @Override
198                 public void onVisualQueryDetectionServiceInitialized(int status) {
199                     Log.i(mTag, "onVisualQueryDetectionServiceInitialized status = " + status);
200                 }
201 
202                 @Override
203                 public void onVisualQueryDetectionServiceRestarted() {
204                     //No-op
205                 }
206 
207                 @Override
208                 public void onFailure(
209                         VisualQueryDetectionServiceFailure visualQueryDetectionServiceFailure) {
210                     //No-op
211                 }
212 
213                 @Override
214                 public void onUnknownFailure(String errorMessage) {
215                     //No-op
216                 }
217             };
218 
219 
220     @Override
onReady()221     public void onReady() {
222         super.onReady();
223         Log.d(mTag, "onReady()");
224         sService = this;
225         if (sConnectLatch != null) {
226             sConnectLatch.countDown();
227         }
228     }
229 
230     @Override
onShutdown()231     public void onShutdown() {
232         super.onShutdown();
233         Log.d(mTag, "onShutdown()");
234         if (sDisconnectLatch != null) {
235             sDisconnectLatch.countDown();
236         }
237     }
238 
239     @Override
onPrepareToShowSession(Bundle args, int flags)240     public void onPrepareToShowSession(Bundle args, int flags) {
241         Log.d(mTag, "onPrepareToShowSession args = " + args + ", flags =" + flags);
242         if (sPrepareToShowSessionLatch != null) {
243             sPrepareToShowSessionLatch.countDown();
244         }
245         mPrepareToShowSessionArgs = args;
246         mPrepareToShowSessionFlags = flags;
247     }
248 
249     @Override
onShowSessionFailed(Bundle args)250     public void onShowSessionFailed(Bundle args) {
251         Log.d(mTag, "onShowSessionFailed args = " + args);
252         if (sShowSessionFailedLatch != null) {
253             sShowSessionFailedLatch.countDown();
254         }
255         mShowSessionFailedArgs = args;
256     }
257 
258     /**
259      * Clear the non-static state of this class
260      */
resetState()261     public void resetState() {
262         mDetectorInitializedLatch = null;
263         mVisualQueryDetector = null;
264         mSoftwareHotwordDetector = null;
265         mAlwaysOnHotwordDetector = null;
266         mIsCreateDetectorIllegalStateExceptionThrow = false;
267         mIsDetectorCallbackRunningOnMainThread = false;
268         mIsCreateDetectorSecurityExceptionThrow = false;
269         mPrepareToShowSessionArgs = new Bundle();
270         mShowSessionFailedArgs = new Bundle();
271         mPrepareToShowSessionFlags = -1;
272         mInitializedStatus = STATUS_NO_CALLBACK_CALLED;
273         mAvailabilityStatus = STATUS_NO_CALLBACK_CALLED;
274     }
275 
276     /**
277      * Reset the static variables of this service.
278      */
resetStaticValues()279     public static void resetStaticValues() {
280         sService = null;
281         sConnectLatch = null;
282         sDisconnectLatch = null;
283         sPrepareToShowSessionLatch = null;
284         sShowSessionFailedLatch = null;
285     }
286 
287     /**
288      * Init the CountDownLatch that is used to wait for service onReady() and onShutdown().
289      */
initServiceConnectionLatches()290     public static void initServiceConnectionLatches() {
291         sConnectLatch = new CountDownLatch(1);
292         sDisconnectLatch = new CountDownLatch(1);
293     }
294 
295     /**
296      * Init the CountDownLatch that is used to wait for onPrepareToShowSession and
297      * onShowSessionFailed
298      */
initShowSessionLatch()299     public static void initShowSessionLatch() {
300         sPrepareToShowSessionLatch = new CountDownLatch(1);
301         sShowSessionFailedLatch = new CountDownLatch(1);
302     }
303 
304     /**
305      * Wait for service onReady().
306      */
waitServiceConnect()307     public static void waitServiceConnect() throws InterruptedException {
308         if (sConnectLatch == null) {
309             throw new AssertionError("Should init connect CountDownLatch");
310         }
311         if (!sConnectLatch.await(WAIT_TIMEOUT_IN_MS, TimeUnit.MILLISECONDS)) {
312             throw new AssertionError("VoiceInteractionService doesn't start.");
313         }
314         sConnectLatch = null;
315     }
316 
317     /**
318      * Wait for service onShutdown().
319      */
waitServiceDisconnect()320     public static void waitServiceDisconnect() throws InterruptedException {
321         if (sDisconnectLatch == null) {
322             throw new AssertionError("Should init disconnect CountDownLatch");
323         }
324         if (!sDisconnectLatch.await(WAIT_TIMEOUT_IN_MS, TimeUnit.MILLISECONDS)) {
325             throw new AssertionError("VoiceInteractionService doesn't shut down.");
326         }
327         sDisconnectLatch = null;
328     }
329 
330     /**
331      * Wait for onPrepareToShowSession
332      */
waitOnPrepareToShowSession()333     public static void waitOnPrepareToShowSession() throws InterruptedException {
334         if (sPrepareToShowSessionLatch == null) {
335             throw new AssertionError("Should init prepareToShowSession CountDownLatch");
336         }
337         if (!sPrepareToShowSessionLatch.await(WAIT_TIMEOUT_IN_MS, TimeUnit.MILLISECONDS)) {
338             throw new AssertionError("onPrepareToShowSession has not been triggered");
339         }
340         sPrepareToShowSessionLatch = null;
341     }
342 
343     /**
344      * Wait for onShowSessionFailed
345      */
waitOnShowSessionFailed()346     public static void waitOnShowSessionFailed() throws InterruptedException {
347         if (sShowSessionFailedLatch == null) {
348             throw new AssertionError("Should init showSessionFailed CountDownLatch");
349         }
350         if (!sShowSessionFailedLatch.await(WAIT_TIMEOUT_IN_MS, TimeUnit.MILLISECONDS)) {
351             throw new AssertionError("onShowSessionFailed has not been triggered");
352         }
353         sShowSessionFailedLatch = null;
354     }
355 
356     /**
357      * Returns {@link VoiceInteractionService} for testing
358      */
getService()359     public static VoiceInteractionService getService() {
360         return sService;
361     }
362 
363     /**
364      * Returns if the {@link VoiceInteractionService} is running on the main thread
365      */
isRunningOnMainThread()366     static boolean isRunningOnMainThread() {
367         return Looper.getMainLooper().getThread() == Thread.currentThread();
368     }
369 
370     /**
371      * Returns the {@link Executor} of the detector callback
372      */
getDetectorCallbackExecutor()373     public static Executor getDetectorCallbackExecutor() {
374         return Executors.newSingleThreadExecutor();
375     }
376 
377     /**
378      * Returns if createAlwaysOnHotwordDetector throws IllegalStateException.
379      */
isCreateDetectorIllegalStateExceptionThrow()380     public boolean isCreateDetectorIllegalStateExceptionThrow() {
381         Log.d(mTag, "isCreateDetectorIllegalStateExceptionThrow = "
382                 + mIsCreateDetectorIllegalStateExceptionThrow);
383         return mIsCreateDetectorIllegalStateExceptionThrow;
384     }
385 
386     /**
387      * Returns if createAlwaysOnHotwordDetector throws SecurityException.
388      */
isCreateDetectorSecurityExceptionThrow()389     public boolean isCreateDetectorSecurityExceptionThrow() {
390         Log.d(mTag, "isCreateDetectorSecurityExceptionThrow = "
391                 + mIsCreateDetectorSecurityExceptionThrow);
392         return mIsCreateDetectorSecurityExceptionThrow;
393     }
394 
395     /**
396      * Returns if the callback of the detector is running on main thread
397      */
isDetectorCallbackRunningOnMainThread()398     public boolean isDetectorCallbackRunningOnMainThread() {
399         Log.d(mTag, "isDetectorCallbackRunningOnMainThread = "
400                 + mIsDetectorCallbackRunningOnMainThread);
401         return mIsDetectorCallbackRunningOnMainThread;
402     }
403 
404     /**
405      * Set the value to mIsDetectorCallbackRunningOnMainThread
406      */
setIsDetectorCallbackRunningOnMainThread( boolean isDetectorCallbackRunningOnMainThread)407     public void setIsDetectorCallbackRunningOnMainThread(
408             boolean isDetectorCallbackRunningOnMainThread) {
409         mIsDetectorCallbackRunningOnMainThread = isDetectorCallbackRunningOnMainThread;
410     }
411 
412     /**
413      * Returns the Service's AlwaysOnHotwordDetector.
414      */
getAlwaysOnHotwordDetector()415     public AlwaysOnHotwordDetector getAlwaysOnHotwordDetector() {
416         return mAlwaysOnHotwordDetector;
417     }
418 
419     /**
420      * Returns the Service's SoftwareHotwordDetector.
421      */
getSoftwareHotwordDetector()422     public HotwordDetector getSoftwareHotwordDetector() {
423         return mSoftwareHotwordDetector;
424     }
425 
426     /**
427      * Returns the Service's VisualQueryDetector.
428      */
getVisualQueryDetector()429     public VisualQueryDetector getVisualQueryDetector() {
430         return mVisualQueryDetector;
431     }
432 
433     /**
434      * Wait for createAlwaysOnHotwordDetectorNoHotwordDetectionService be ready
435      */
waitCreateAlwaysOnHotwordDetectorNoHotwordDetectionServiceReady()436     public void waitCreateAlwaysOnHotwordDetectorNoHotwordDetectionServiceReady()
437             throws InterruptedException {
438         Log.d(mTag, "waitCreateAlwaysOnHotwordDetectorNoHotwordDetectionServiceReady(), latch="
439                 + mDetectorInitializedLatch);
440         if (mDetectorInitializedLatch == null
441                 || !mDetectorInitializedLatch.await(WAIT_TIMEOUT_IN_MS,
442                 TimeUnit.MILLISECONDS)) {
443             Log.w(mTag, "waitCreateAlwaysOnHotwordDetectorNoHotwordDetectionServiceReady()");
444             mDetectorInitializedLatch = null;
445             throw new AssertionError(
446                     "CreateAlwaysOnHotwordDetectorNoHotwordDetectionService is not ready");
447         }
448         mDetectorInitializedLatch = null;
449     }
450 
451     /**
452      * Wait for onSandboxedDetectionServiceInitialized() be called or exception throws when creating
453      * AlwaysOnHotwordDetector or VisualQueryDetector.
454      */
waitSandboxedDetectionServiceInitializedCalledOrException()455     public void waitSandboxedDetectionServiceInitializedCalledOrException()
456             throws InterruptedException {
457         Log.d(mTag, "waitSandboxedDetectionServiceInitializedCalledOrException(), latch="
458                 + mDetectorInitializedLatch);
459         if (mDetectorInitializedLatch == null
460                 || !mDetectorInitializedLatch.await(WAIT_TIMEOUT_IN_MS,
461                 TimeUnit.MILLISECONDS)) {
462             Log.w(mTag, "waitAndGetSandboxedServiceInitializedResult()");
463             mDetectorInitializedLatch = null;
464             throw new AssertionError("Sandboxed detection service initialized fail.");
465         }
466         mDetectorInitializedLatch = null;
467     }
468 
resetDetectorCreationExceptions()469     private void resetDetectorCreationExceptions() {
470         mIsCreateDetectorIllegalStateExceptionThrow = false;
471         mIsCreateDetectorSecurityExceptionThrow = false;
472     }
473 
474     /**
475      * Return the args from onPrepareToShowSession.
476      */
477     @NonNull
getPrepareToShowSessionArgs()478     public Bundle getPrepareToShowSessionArgs() {
479         Log.d(mTag, "getPrepareToShowSessionArgs = " + mPrepareToShowSessionArgs);
480         return mPrepareToShowSessionArgs;
481     }
482 
483     /**
484      * Return the flags from onPrepareToShowSession.
485      */
getPrepareToShowSessionFlags()486     public int getPrepareToShowSessionFlags() {
487         Log.d(mTag, "getPrepareToShowSessionFlags = " + mPrepareToShowSessionFlags);
488         return mPrepareToShowSessionFlags;
489     }
490 
491     /**
492      * Return the args from onShowSessionFailed.
493      */
494     @NonNull
getShowSessionFailedArgs()495     public Bundle getShowSessionFailedArgs() {
496         Log.d(mTag, "getShowSessionFailedArgs = " + mShowSessionFailedArgs);
497         return mShowSessionFailedArgs;
498     }
499 
500     /**
501      * Return the result for onHotwordDetectionServiceInitialized().
502      */
getSandboxedDetectionServiceInitializedResult()503     public int getSandboxedDetectionServiceInitializedResult() {
504         Log.d(mTag, "getSandboxedDetectionServiceInitializedResult = " + mInitializedStatus);
505         return mInitializedStatus;
506     }
507 
508 
509     /**
510      * Enable override the model enrollment database one which has a specific mode registered.
511      * Need to have "android.permission.MANAGE_VOICE_KEYPHRASES" permission.
512      *
513      * @see #disableOverrideRegisterModel()
514      */
enableOverrideRegisterModel(KeyphraseSoundModel model)515     public void enableOverrideRegisterModel(KeyphraseSoundModel model) {
516         final KeyphraseModelManager manager = createKeyphraseModelManager();
517         manager.setModelDatabaseForTestEnabled(/* enabled= */ true);
518         manager.updateKeyphraseSoundModel(model);
519     }
520 
521     /**
522      * Disable model override started in
523      * {@link #enableOverrideRegisterModel(KeyphraseSoundModel)}
524      */
disableOverrideRegisterModel()525     public void disableOverrideRegisterModel() {
526         createKeyphraseModelManager().setModelDatabaseForTestEnabled(/* enabled= */ false);
527     }
528 
529     /**
530      * Creates a file in the internal storage to test the file read method
531      * {@link android.service.voice.VisualQueryDetectionService#openFileInput(String)}.
532      * @throws Throwable throws exceptions when writing to the file via output stream.
533      */
createTestFile(String suffix)534     public void createTestFile(String suffix) throws Throwable {
535         File path = this.getFilesDir();
536         File file = new File(path, Utils.TEST_RESOURCE_FILE_NAME + suffix);
537         try (FileOutputStream stream = new FileOutputStream(file)) {
538             stream.write(Utils.TEST_RESOURCE_FILE_CONTENT.getBytes());
539         }
540     }
541 
542     /**
543      * Remove all files in the internal storage that is created by
544      * {@link BaseVoiceInteractionService#createTestFile(String)}.
545      */
removeTestFiles()546     public void removeTestFiles() {
547         File path = this.getFilesDir();
548         File[] files = path.listFiles();
549         if (files != null) {
550             for (File file : files) {
551                 // Check if the filename starts with the specified prefix
552                 if (file.getName().startsWith(Utils.TEST_RESOURCE_FILE_NAME)) {
553                     file.delete(); // Delete the file
554                 }
555             }
556         }
557     }
558 
callCreateAlwaysOnHotwordDetectorNoHotwordDetectionService( AlwaysOnHotwordDetector.Callback callback, boolean useExecutor)559     AlwaysOnHotwordDetector callCreateAlwaysOnHotwordDetectorNoHotwordDetectionService(
560             AlwaysOnHotwordDetector.Callback callback, boolean useExecutor) {
561         Log.i(mTag,
562                 "callCreateAlwaysOnHotwordDetectorNoHotwordDetectionService() useExecutor = "
563                         + useExecutor);
564         try {
565             setTestModuleForAlwaysOnHotwordDetectorEnabled(true);
566             resetDetectorCreationExceptions();
567             if (useExecutor) {
568                 return createAlwaysOnHotwordDetector(KEYPHRASE_TEXT,
569                         KEYPHRASE_LOCALE,
570                         getDetectorCallbackExecutor(),
571                         callback);
572             }
573             return createAlwaysOnHotwordDetector(KEYPHRASE_TEXT, KEYPHRASE_LOCALE, callback);
574         } catch (IllegalStateException | SecurityException e) {
575             if (e instanceof IllegalStateException) {
576                 mIsCreateDetectorIllegalStateExceptionThrow = true;
577             } else {
578                 mIsCreateDetectorSecurityExceptionThrow = true;
579             }
580             Log.w(mTag, "callCreateAlwaysOnHotwordDetector() exception: ", e);
581             if (mDetectorInitializedLatch != null) {
582                 mDetectorInitializedLatch.countDown();
583             }
584         } finally {
585             setTestModuleForAlwaysOnHotwordDetectorEnabled(false);
586         }
587         return null;
588     }
589 
callCreateAlwaysOnHotwordDetector( AlwaysOnHotwordDetector.Callback callback)590     AlwaysOnHotwordDetector callCreateAlwaysOnHotwordDetector(
591             AlwaysOnHotwordDetector.Callback callback) {
592         return callCreateAlwaysOnHotwordDetector(callback, /* useExecutor= */ false);
593     }
594 
callCreateAlwaysOnHotwordDetector( AlwaysOnHotwordDetector.Callback callback, boolean useExecutor)595     AlwaysOnHotwordDetector callCreateAlwaysOnHotwordDetector(
596             AlwaysOnHotwordDetector.Callback callback, boolean useExecutor) {
597         return callCreateAlwaysOnHotwordDetector(callback, useExecutor, /* options= */ null);
598     }
599 
callCreateAlwaysOnHotwordDetector( AlwaysOnHotwordDetector.Callback callback, boolean useExecutor, @Nullable PersistableBundle options)600     AlwaysOnHotwordDetector callCreateAlwaysOnHotwordDetector(
601             AlwaysOnHotwordDetector.Callback callback, boolean useExecutor,
602             @Nullable PersistableBundle options) {
603         Log.i(mTag,
604                 "callCreateAlwaysOnHotwordDetector() useExecutor = " + useExecutor + ", options = "
605                         + options);
606         try {
607             setTestModuleForAlwaysOnHotwordDetectorEnabled(true);
608             resetDetectorCreationExceptions();
609             if (useExecutor) {
610                 return createAlwaysOnHotwordDetector(KEYPHRASE_TEXT,
611                         KEYPHRASE_LOCALE,
612                         options != null ? options : Helper.createFakePersistableBundleData(),
613                         Helper.createFakeSharedMemoryData(),
614                         getDetectorCallbackExecutor(),
615                         callback);
616             }
617             return createAlwaysOnHotwordDetector(KEYPHRASE_TEXT,
618                     KEYPHRASE_LOCALE,
619                     options != null ? options : Helper.createFakePersistableBundleData(),
620                     Helper.createFakeSharedMemoryData(),
621                     callback);
622         } catch (IllegalStateException | SecurityException e) {
623             if (e instanceof IllegalStateException) {
624                 mIsCreateDetectorIllegalStateExceptionThrow = true;
625             } else {
626                 mIsCreateDetectorSecurityExceptionThrow = true;
627             }
628             Log.w(mTag, "callCreateAlwaysOnHotwordDetector() exception: ", e);
629             if (mDetectorInitializedLatch != null) {
630                 mDetectorInitializedLatch.countDown();
631             }
632         } finally {
633             setTestModuleForAlwaysOnHotwordDetectorEnabled(false);
634         }
635         return null;
636     }
637 
callCreateSoftwareHotwordDetector(HotwordDetector.Callback callback, boolean useExecutor)638     HotwordDetector callCreateSoftwareHotwordDetector(HotwordDetector.Callback callback,
639             boolean useExecutor) {
640         return callCreateSoftwareHotwordDetector(callback, useExecutor, /* options= */ null);
641     }
642 
callCreateSoftwareHotwordDetector(HotwordDetector.Callback callback, boolean useExecutor, @Nullable PersistableBundle options)643     HotwordDetector callCreateSoftwareHotwordDetector(HotwordDetector.Callback callback,
644             boolean useExecutor, @Nullable PersistableBundle options) {
645         Log.i(mTag,
646                 "callCreateSoftwareHotwordDetector() useExecutor = " + useExecutor + ", options = "
647                         + options);
648         try {
649             resetDetectorCreationExceptions();
650             if (useExecutor) {
651                 return createHotwordDetector(
652                         options != null ? options : Helper.createFakePersistableBundleData(),
653                         Helper.createFakeSharedMemoryData(), getDetectorCallbackExecutor(),
654                         callback);
655             }
656             return createHotwordDetector(
657                     options != null ? options : Helper.createFakePersistableBundleData(),
658                     Helper.createFakeSharedMemoryData(), callback);
659         } catch (IllegalStateException | SecurityException e) {
660             if (e instanceof IllegalStateException) {
661                 mIsCreateDetectorIllegalStateExceptionThrow = true;
662             } else {
663                 mIsCreateDetectorSecurityExceptionThrow = true;
664             }
665             Log.w(mTag, "callCreateSoftwareHotwordDetector() exception: " + e);
666             if (mDetectorInitializedLatch != null) {
667                 mDetectorInitializedLatch.countDown();
668             }
669         }
670         return null;
671     }
672 
callCreateVisualQueryDetector(VisualQueryDetector.Callback callback)673     VisualQueryDetector callCreateVisualQueryDetector(VisualQueryDetector.Callback callback) {
674         Log.i(mTag, "callCreateVisualQueryDetector()");
675         try {
676             resetDetectorCreationExceptions();
677             return createVisualQueryDetector(Helper.createFakePersistableBundleData(),
678                     Helper.createFakeSharedMemoryData(), Executors.newSingleThreadExecutor(),
679                     callback);
680         } catch (IllegalStateException | SecurityException e) {
681             if (e instanceof IllegalStateException) {
682                 mIsCreateDetectorIllegalStateExceptionThrow = true;
683             } else {
684                 mIsCreateDetectorSecurityExceptionThrow = true;
685             }
686             Log.w(mTag, "callCreateVisualQueryDetector() exception: " + e);
687             if (mDetectorInitializedLatch != null) {
688                 mDetectorInitializedLatch.countDown();
689             }
690         }
691         return null;
692     }
693 }
694