• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 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 package android.voiceinteraction.common;
17 
18 import static android.service.voice.HotwordAudioStream.KEY_AUDIO_STREAM_COPY_BUFFER_LENGTH_BYTES;
19 
20 import android.app.VoiceInteractor.PickOptionRequest.Option;
21 import android.content.LocusId;
22 import android.media.AudioFormat;
23 import android.media.AudioTimestamp;
24 import android.os.Bundle;
25 import android.os.Parcel;
26 import android.os.ParcelFileDescriptor;
27 import android.os.Parcelable;
28 import android.os.PersistableBundle;
29 import android.os.SystemProperties;
30 import android.service.voice.HotwordAudioStream;
31 import android.service.voice.HotwordDetectedResult;
32 import android.util.Log;
33 
34 import com.android.compatibility.common.util.PropertyUtil;
35 
36 import java.io.IOException;
37 import java.io.OutputStream;
38 import java.util.ArrayList;
39 import java.util.List;
40 import java.util.Objects;
41 import java.util.concurrent.CountDownLatch;
42 import java.util.concurrent.TimeUnit;
43 import java.util.concurrent.locks.Condition;
44 
45 public class Utils {
46     public enum TestCaseType {
47         COMPLETION_REQUEST_TEST,
48         COMPLETION_REQUEST_CANCEL_TEST,
49         CONFIRMATION_REQUEST_TEST,
50         CONFIRMATION_REQUEST_CANCEL_TEST,
51         ABORT_REQUEST_TEST,
52         ABORT_REQUEST_CANCEL_TEST,
53         PICKOPTION_REQUEST_TEST,
54         PICKOPTION_REQUEST_CANCEL_TEST,
55         COMMANDREQUEST_TEST,
56         COMMANDREQUEST_CANCEL_TEST,
57         SUPPORTS_COMMANDS_TEST
58     }
59 
60     private static final String TAG = Utils.class.getSimpleName();
61 
62     public static final long OPERATION_TIMEOUT_MS = 5000;
63 
64     /** CDD restricts the max size of each successful hotword result is 100 bytes. */
65     public static final int MAX_HOTWORD_DETECTED_RESULT_SIZE = 100;
66 
67     /**
68      * Limits the max value for the hotword offset.
69      *
70      * Note: Must match the definition in
71      * frameworks/base/core/java/android/service/voice/HotwordDetectedResult.java.
72      */
73     public static final int LIMIT_HOTWORD_OFFSET_MAX_VALUE = 60 * 60 * 1000; // 1 hour
74 
75     /**
76      * Limits the max value for the triggered audio channel.
77      *
78      * Note: Must match the definition in
79      * frameworks/base/core/java/android/service/voice/HotwordDetectedResult.java.
80      */
81     public static final int LIMIT_AUDIO_CHANNEL_MAX_VALUE = 63;
82 
83     /**
84      * Indicate which test event for testing.
85      *
86      * Note: The VIS is the abbreviation of VoiceInteractionService
87      */
88     public static final int VIS_NORMAL_TEST = 0;
89 
90     /** Indicate which test scenario for testing. */
91     public static final int EXTRA_HOTWORD_DETECTION_SERVICE_ON_UPDATE_STATE_CRASH = 1;
92     public static final int EXTRA_HOTWORD_DETECTION_SERVICE_ON_UPDATE_STATE_UNEXPECTED_CALLBACK = 2;
93     public static final int EXTRA_HOTWORD_DETECTION_SERVICE_SEND_OVER_MAX_INIT_STATUS = 3;
94     public static final int EXTRA_HOTWORD_DETECTION_SERVICE_SEND_CUSTOM_INIT_STATUS = 4;
95     public static final int EXTRA_HOTWORD_DETECTION_SERVICE_ENABLE_AUDIO_EGRESS = 5;
96     public static final int EXTRA_HOTWORD_DETECTION_SERVICE_CLEAR_SOFTWARE_DETECTION_JOB = 6;
97     public static final int EXTRA_HOTWORD_DETECTION_SERVICE_NO_NEED_ACTION_DURING_DETECTION = 7;
98     // test scenario to verify the HotwordDetectionService was created after a given time
99     // This can be used to verify the service was restarted or recreated.
100     public static final int EXTRA_HOTWORD_DETECTION_SERVICE_SEND_SUCCESS_IF_CREATED_AFTER = 8;
101     // Check the HotwordDetectionService can read audio and the data is not zero
102     public static final int EXTRA_HOTWORD_DETECTION_SERVICE_CAN_READ_AUDIO_DATA_IS_NOT_ZERO = 9;
103 
104     /** Indicate to start a new activity for testing. */
105     public static final int ACTIVITY_NEW = 0;
106     /** Indicate to finish an activity for testing. */
107     public static final int ACTIVITY_FINISH = 1;
108     /** Indicate to crash an activity for testing. */
109     public static final int ACTIVITY_CRASH = 2;
110 
111     /** Indicate what kind of parameters for calling registerVisibleActivityCallback. */
112     public static final int VISIBLE_ACTIVITY_CALLBACK_REGISTER_NORMAL = 0;
113     public static final int VISIBLE_ACTIVITY_CALLBACK_REGISTER_WITHOUT_EXECUTOR = 1;
114     public static final int VISIBLE_ACTIVITY_CALLBACK_REGISTER_WITHOUT_CALLBACK = 2;
115 
116     public static final int NUM_TEST_RESOURCE_FILE_MULTIPLE = 50;
117 
118     public static final String TEST_APP_PACKAGE = "android.voiceinteraction.testapp";
119     public static final String TESTCASE_TYPE = "testcase_type";
120     public static final String TESTINFO = "testinfo";
121     public static final String BROADCAST_INTENT = "android.intent.action.VOICE_TESTAPP";
122     public static final String TEST_PROMPT = "testprompt";
123     public static final String PICKOPTON_1 = "one";
124     public static final String PICKOPTON_2 = "two";
125     public static final String PICKOPTON_3 = "3";
126     public static final String TEST_COMMAND = "test_command";
127     public static final String TEST_ONCOMMAND_RESULT = "test_oncommand_result";
128     public static final String TEST_ONCOMMAND_RESULT_VALUE = "test_oncommand_result value";
129 
130     public static final String CONFIRMATION_REQUEST_SUCCESS = "confirmation ok";
131     public static final String COMPLETION_REQUEST_SUCCESS = "completion ok";
132     public static final String ABORT_REQUEST_SUCCESS = "abort ok";
133     public static final String PICKOPTION_REQUEST_SUCCESS = "pickoption ok";
134     public static final String COMMANDREQUEST_SUCCESS = "commandrequest ok";
135     public static final String SUPPORTS_COMMANDS_SUCCESS = "supportsCommands ok";
136 
137     public static final String CONFIRMATION_REQUEST_CANCEL_SUCCESS = "confirm cancel ok";
138     public static final String COMPLETION_REQUEST_CANCEL_SUCCESS = "completion canel ok";
139     public static final String ABORT_REQUEST_CANCEL_SUCCESS = "abort cancel ok";
140     public static final String PICKOPTION_REQUEST_CANCEL_SUCCESS = "pickoption  cancel ok";
141     public static final String COMMANDREQUEST_CANCEL_SUCCESS = "commandrequest cancel ok";
142     public static final String TEST_ERROR = "Error In Test:";
143 
144     public static final String PRIVATE_OPTIONS_KEY = "private_key";
145     public static final String PRIVATE_OPTIONS_VALUE = "private_value";
146 
147     public static final String DIRECT_ACTION_EXTRA_KEY = "directActionExtraKey";
148     public static final String DIRECT_ACTION_EXTRA_VALUE = "directActionExtraValue";
149     public static final String DIRECT_ACTION_FILE_NAME = "directActionFileName";
150     public static final String DIRECT_ACTION_FILE_CONTENT = "directActionFileContent";
151     public static final String DIRECT_ACTION_AUTHORITY =
152             "android.voiceinteraction.testapp.fileprovider";
153 
154     public static final String DIRECT_ACTIONS_KEY_CANCEL_CALLBACK = "cancelCallback";
155     public static final String DIRECT_ACTIONS_KEY_RESULT = "result";
156 
157     public static final String DIRECT_ACTIONS_SESSION_CMD_PERFORM_ACTION = "performAction";
158     public static final String DIRECT_ACTIONS_SESSION_CMD_PERFORM_ACTION_CANCEL =
159             "performActionCancel";
160     public static final String DIRECT_ACTIONS_SESSION_CMD_DETECT_ACTIONS_CHANGED =
161             "detectActionsChanged";
162     public static final String DIRECT_ACTIONS_SESSION_CMD_GET_ACTIONS = "getActions";
163 
164     public static final String DIRECT_ACTIONS_ACTIVITY_CMD_DESTROYED_INTERACTOR =
165             "destroyedInteractor";
166     public static final String DIRECT_ACTIONS_ACTIVITY_CMD_INVALIDATE_ACTIONS = "invalidateActions";
167     public static final String DIRECT_ACTIONS_ACTIVITY_CMD_GET_PACKAGE_NAME = "getpackagename";
168     public static final String DIRECT_ACTIONS_ACTIVITY_CMD_GET_PACKAGE_INFO = "getpackageinfo";
169 
170     public static final String DIRECT_ACTIONS_RESULT_PERFORMED = "performed";
171     public static final String DIRECT_ACTIONS_RESULT_CANCELLED = "cancelled";
172     public static final String DIRECT_ACTIONS_RESULT_EXECUTING = "executing";
173 
174     public static final String DIRECT_ACTIONS_ACTION_ID = "actionId";
175     public static final Bundle DIRECT_ACTIONS_ACTION_EXTRAS = new Bundle();
176     static {
DIRECT_ACTIONS_ACTION_EXTRAS.putString(DIRECT_ACTION_EXTRA_KEY, DIRECT_ACTION_EXTRA_VALUE)177         DIRECT_ACTIONS_ACTION_EXTRAS.putString(DIRECT_ACTION_EXTRA_KEY,
178                 DIRECT_ACTION_EXTRA_VALUE);
179     }
180     public static final LocusId DIRECT_ACTIONS_LOCUS_ID = new LocusId("locusId");
181 
182     public static final String SERVICE_NAME =
183             "android.voiceinteraction.service/.MainInteractionService";
184 
185     public static final String KEY_TEST_EVENT = "testEvent";
186     public static final String KEY_TEST_RESULT = "testResult";
187     public static final String KEY_TEST_SCENARIO = "testScenario";
188     public static final String KEY_DETECTION_DELAY_MS = "detectionDelayMs";
189     public static final String KEY_DETECTION_REJECTED = "detection_rejected";
190     public static final String KEY_INITIALIZATION_STATUS = "initialization_status";
191     /**
192      * It only works when the test scenario is
193      * {@link #EXTRA_HOTWORD_DETECTION_SERVICE_ENABLE_AUDIO_EGRESS}
194      *
195      * Type: Boolean
196      */
197     public static final String KEY_AUDIO_EGRESS_USE_ILLEGAL_COPY_BUFFER_SIZE =
198             "useIllegalCopyBufferSize";
199     public static final String KEY_TIMESTAMP_MILLIS = "timestamp_millis";
200 
201     public static final String VOICE_INTERACTION_KEY_CALLBACK = "callback";
202     public static final String VOICE_INTERACTION_KEY_CONTROL = "control";
203     public static final String VOICE_INTERACTION_KEY_COMMAND = "command";
204     public static final String VOICE_INTERACTION_KEY_TASKID = "taskId";
205     public static final String VOICE_INTERACTION_DIRECT_ACTIONS_KEY_ACTION = "action";
206     public static final String VOICE_INTERACTION_KEY_ARGUMENTS = "arguments";
207     public static final String VOICE_INTERACTION_KEY_CLASS = "class";
208 
209     public static final String VOICE_INTERACTION_KEY_REMOTE_CALLBACK_FOR_NEW_SESSION =
210             "remoteCallbackForNewSession";
211     public static final String VOICE_INTERACTION_KEY_USE_ACTIVITY_OPTIONS = "useActivityOptions";
212     public static final String VOICE_INTERACTION_SESSION_CMD_FINISH = "hide";
213     public static final String VOICE_INTERACTION_ACTIVITY_CMD_FINISH = "finish";
214     public static final String VOICE_INTERACTION_ACTIVITY_CMD_CRASH = "crash";
215 
216     // For v2 reliable visible activity lookup feature
217     public static final String VISIBLE_ACTIVITY_CALLBACK_ONVISIBLE_INTENT =
218             "android.intent.action.VISIBLE_ACTIVITY_CALLBACK_ONVISIBLE_INTENT";
219     public static final String VISIBLE_ACTIVITY_CALLBACK_ONINVISIBLE_INTENT =
220             "android.intent.action.VISIBLE_ACTIVITY_CALLBACK_ONINVISIBLE_INTENT";
221     public static final String VISIBLE_ACTIVITY_KEY_RESULT = "result";
222 
223     public static final String VISIBLE_ACTIVITY_CMD_REGISTER_CALLBACK = "registerCallback";
224     public static final String VISIBLE_ACTIVITY_CMD_UNREGISTER_CALLBACK = "unregisterCallback";
225 
226     // For asking to bind to a test VoiceInteractionService if it supports it
227     public static final String ACTION_BIND_TEST_VOICE_INTERACTION =
228             "android.intent.action.ACTION_BIND_TEST_VOICE_INTERACTION";
229     public static final String TEST_VOICE_INTERACTION_SERVICE_PACKAGE_NAME =
230             "android.voiceinteraction.service";
231     public static final String PROXY_VOICE_INTERACTION_SERVICE_CLASS_NAME =
232             "android.voiceinteraction.service.ProxyVoiceInteractionService";
233     public static final String PROXY_VOICEINTERACTION_SERVICE_COMPONENT =
234             TEST_VOICE_INTERACTION_SERVICE_PACKAGE_NAME + "/"
235                     + PROXY_VOICE_INTERACTION_SERVICE_CLASS_NAME;
236     public static final String VOICE_INTERACTION_SERVICE_BINDING_HELPER_CLASS_NAME =
237             "android.voiceinteraction.service.VoiceInteractionServiceBindingHelper";
238     // File opening related
239     public static final String TEST_RESOURCE_FILE_NAME = "test_resource";
240     public static final String TEST_RESOURCE_FILE_CONTENT = "This file contains test resource";
241     private static final String KEY_FAKE_DATA = "fakeData";
242     private static final String VALUE_FAKE_DATA = "fakeData";
243 
244     private static final long FRAME_POSITION = 0;
245     private static final long NANO_TIME_NS = 1000;
246 
247     private static final byte[] FAKE_HOTWORD_AUDIO_DATA =
248             new byte[]{'h', 'o', 't', 'w', 'o', 'r', 'd', '!'};
249 
250     private static final HotwordAudioStream HOTWORD_AUDIO_STREAM =
251             new HotwordAudioStream.Builder(createFakeAudioFormat(), createFakeAudioStream())
252                     .setInitialAudio(FAKE_HOTWORD_AUDIO_DATA)
253                     .setMetadata(createFakePersistableBundleData())
254                     .setTimestamp(createFakeAudioTimestamp())
255                     .build();
256 
257     private static final HotwordAudioStream HOTWORD_AUDIO_STREAM_WRONG_COPY_BUFFER_SIZE =
258             new HotwordAudioStream.Builder(createFakeAudioFormat(), createFakeAudioStream())
259                     .setInitialAudio(FAKE_HOTWORD_AUDIO_DATA)
260                     .setMetadata(createFakePersistableBundleData(0))
261                     .setTimestamp(createFakeAudioTimestamp())
262                     .build();
263 
264     public static final HotwordDetectedResult AUDIO_EGRESS_DETECTED_RESULT =
265             new HotwordDetectedResult.Builder().setAudioStreams(
266                     List.of(HOTWORD_AUDIO_STREAM)).build();
267 
268     public static final HotwordDetectedResult AUDIO_EGRESS_DETECTED_RESULT_WRONG_COPY_BUFFER_SIZE =
269             new HotwordDetectedResult.Builder().setAudioStreams(
270                     List.of(HOTWORD_AUDIO_STREAM_WRONG_COPY_BUFFER_SIZE)).build();
271 
272     public static final boolean SYSPROP_VISUAL_QUERY_SERVICE_ENABLED =
273             SystemProperties.getBoolean("ro.hotword.visual_query_service_enabled", false);
274 
275     /**
276      * Returns the PersistableBundle data that is used for testing.
277      */
createFakePersistableBundleData()278     private static PersistableBundle createFakePersistableBundleData() {
279         return createFakePersistableBundleData(/* copyBufferSize= */ -1);
280     }
281 
282     /**
283      * Returns the PersistableBundle data that is used for testing.
284      */
createFakePersistableBundleData(int copyBufferSize)285     private static PersistableBundle createFakePersistableBundleData(int copyBufferSize) {
286         // TODO : Add more data for testing
287         PersistableBundle persistableBundle = new PersistableBundle();
288         persistableBundle.putString(KEY_FAKE_DATA, VALUE_FAKE_DATA);
289         if (copyBufferSize > -1) {
290             persistableBundle.putInt(KEY_AUDIO_STREAM_COPY_BUFFER_LENGTH_BYTES, copyBufferSize);
291         }
292         return persistableBundle;
293     }
294 
295     /**
296      * Returns the AudioFormat data that is used for testing.
297      */
createFakeAudioFormat()298     private static AudioFormat createFakeAudioFormat() {
299         return new AudioFormat.Builder()
300                 .setSampleRate(32000)
301                 .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
302                 .setChannelMask(AudioFormat.CHANNEL_IN_MONO).build();
303     }
304 
305     /**
306      * Returns the ParcelFileDescriptor data that is used for testing.
307      */
createFakeAudioStream()308     private static ParcelFileDescriptor createFakeAudioStream() {
309         ParcelFileDescriptor[] tempParcelFileDescriptors = null;
310         try {
311             tempParcelFileDescriptors = ParcelFileDescriptor.createPipe();
312             try (OutputStream fos =
313                          new ParcelFileDescriptor.AutoCloseOutputStream(
314                                  tempParcelFileDescriptors[1])) {
315                 fos.write(FAKE_HOTWORD_AUDIO_DATA, 0, 8);
316             } catch (IOException e) {
317                 Log.w(TAG, "Failed to pipe audio data : ", e);
318                 throw new IllegalStateException();
319             }
320             return tempParcelFileDescriptors[0];
321         } catch (IOException e) {
322             Log.w(TAG, "Failed to create a pipe : " + e);
323         }
324         throw new IllegalStateException();
325     }
326 
327     /**
328      * Returns the AudioTimestamp for test
329      */
createFakeAudioTimestamp()330     private static AudioTimestamp createFakeAudioTimestamp() {
331         final AudioTimestamp timestamp = new AudioTimestamp();
332         timestamp.framePosition = FRAME_POSITION;
333         timestamp.nanoTime = NANO_TIME_NS;
334         return timestamp;
335     }
336 
toBundleString(Bundle bundle)337     public static final String toBundleString(Bundle bundle) {
338         if (bundle == null) {
339             return "null_bundle";
340         }
341         StringBuffer buf = new StringBuffer("Bundle[ ");
342         String testType = bundle.getString(TESTCASE_TYPE);
343         boolean empty = true;
344         if (testType != null) {
345             empty = false;
346             buf.append("testcase type = " + testType);
347         }
348         ArrayList<String> info = bundle.getStringArrayList(TESTINFO);
349         if (info != null) {
350             for (String s : info) {
351                 empty = false;
352                 buf.append(s + "\n\t\t");
353             }
354         } else {
355             for (String key : bundle.keySet()) {
356                 empty = false;
357                 Object value = bundle.get(key);
358                 if (value instanceof Bundle) {
359                     value = toBundleString((Bundle) value);
360                 }
361                 buf.append(key).append('=').append(value).append(' ');
362             }
363         }
364         return empty ? "empty_bundle" : buf.append(']').toString();
365     }
366 
toOptionsString(Option[] options)367     public static final String toOptionsString(Option[] options) {
368         StringBuilder sb = new StringBuilder();
369         sb.append("{");
370         for (int i = 0; i < options.length; i++) {
371             if (i >= 1) {
372                 sb.append(", ");
373             }
374             sb.append(options[i].getLabel());
375         }
376         sb.append("}");
377         return sb.toString();
378     }
379 
addErrorResult(final Bundle testinfo, final String msg)380     public static final void addErrorResult(final Bundle testinfo, final String msg) {
381         testinfo.getStringArrayList(testinfo.getString(Utils.TESTCASE_TYPE))
382                 .add(TEST_ERROR + " " + msg);
383     }
384 
await(CountDownLatch latch)385     public static boolean await(CountDownLatch latch) {
386         try {
387             if (latch.await(OPERATION_TIMEOUT_MS, TimeUnit.MILLISECONDS)) return true;
388             Log.e(TAG, "latch timed out");
389         } catch (InterruptedException e) {
390             /* ignore */
391             Log.e(TAG, "Interrupted", e);
392         }
393         return false;
394     }
395 
await(Condition condition)396     public static boolean await(Condition condition) {
397         try {
398             if (condition.await(OPERATION_TIMEOUT_MS, TimeUnit.MILLISECONDS)) return true;
399             Log.e(TAG, "condition timed out");
400         } catch (InterruptedException e) {
401             /* ignore */
402             Log.e(TAG, "Interrupted", e);
403         }
404         return false;
405     }
406 
getParcelableSize(Parcelable parcelable)407     public static int getParcelableSize(Parcelable parcelable) {
408         final Parcel p = Parcel.obtain();
409         parcelable.writeToParcel(p, 0);
410         p.setDataPosition(0);
411         final int size = p.dataSize();
412         p.recycle();
413         return size;
414     }
415 
bitCount(long value)416     public static int bitCount(long value) {
417         int bits = 0;
418         while (value > 0) {
419             bits++;
420             value = value >> 1;
421         }
422         return bits;
423     }
424 
isVirtualDevice()425     public static boolean isVirtualDevice() {
426         final String property = PropertyUtil.getProperty("ro.hardware.virtual_device");
427         Log.v(TAG, "virtual device property=" + property);
428         return Objects.equals(property, "1");
429     }
430 }
431