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