1 /* 2 * Copyright (C) 2021 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.audio; 18 19 import static android.media.AudioManager.AUDIO_DEVICE_CATEGORY_HEADPHONES; 20 import static android.media.AudioManager.AUDIO_DEVICE_CATEGORY_UNKNOWN; 21 import static android.media.AudioSystem.isBluetoothDevice; 22 import static android.media.AudioSystem.isBluetoothLeDevice; 23 24 import static com.android.media.audio.Flags.dsaOverBtLeAudio; 25 26 import android.annotation.NonNull; 27 import android.annotation.Nullable; 28 import android.content.Context; 29 import android.content.pm.PackageManager; 30 import android.hardware.Sensor; 31 import android.hardware.SensorManager; 32 import android.media.AudioAttributes; 33 import android.media.AudioDeviceAttributes; 34 import android.media.AudioDeviceInfo; 35 import android.media.AudioFormat; 36 import android.media.AudioSystem; 37 import android.media.INativeSpatializerCallback; 38 import android.media.ISpatializer; 39 import android.media.ISpatializerCallback; 40 import android.media.ISpatializerHeadToSoundStagePoseCallback; 41 import android.media.ISpatializerHeadTrackerAvailableCallback; 42 import android.media.ISpatializerHeadTrackingCallback; 43 import android.media.ISpatializerHeadTrackingModeCallback; 44 import android.media.ISpatializerOutputCallback; 45 import android.media.MediaMetrics; 46 import android.media.Spatializer; 47 import android.media.audio.common.HeadTracking; 48 import android.media.audio.common.Spatialization; 49 import android.os.RemoteCallbackList; 50 import android.os.RemoteException; 51 import android.text.TextUtils; 52 import android.util.Log; 53 import android.util.Pair; 54 import android.util.SparseIntArray; 55 56 import com.android.internal.annotations.GuardedBy; 57 import com.android.internal.annotations.VisibleForTesting; 58 import com.android.server.utils.EventLogger; 59 60 import java.io.PrintWriter; 61 import java.util.ArrayList; 62 import java.util.List; 63 import java.util.Locale; 64 import java.util.UUID; 65 66 /** 67 * A helper class to manage Spatializer related functionality 68 */ 69 public class SpatializerHelper { 70 71 private static final String TAG = "AS.SpatializerHelper"; 72 private static final boolean DEBUG = true; 73 private static final boolean DEBUG_MORE = false; 74 logd(String s)75 private static void logd(String s) { 76 if (DEBUG) { 77 Log.i(TAG, s); 78 } 79 } 80 81 private final @NonNull AudioSystemAdapter mASA; 82 private final @NonNull AudioService mAudioService; 83 private final @NonNull AudioDeviceBroker mDeviceBroker; 84 private @Nullable SensorManager mSensorManager; 85 86 //------------------------------------------------------------ 87 88 /*package*/ static final SparseIntArray SPAT_MODE_FOR_DEVICE_TYPE = new SparseIntArray(14) { 89 { 90 append(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER, Spatialization.Mode.TRANSAURAL); 91 // Speaker safe is considered compatible with spatial audio because routing media usage 92 // to speaker safe only happens in transient situations and should not affect app 93 // decisions to play spatial audio content. 94 append(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER_SAFE, Spatialization.Mode.TRANSAURAL); 95 append(AudioDeviceInfo.TYPE_WIRED_HEADSET, Spatialization.Mode.BINAURAL); 96 append(AudioDeviceInfo.TYPE_WIRED_HEADPHONES, Spatialization.Mode.BINAURAL); 97 // assumption for A2DP: mostly headsets 98 append(AudioDeviceInfo.TYPE_BLUETOOTH_A2DP, Spatialization.Mode.BINAURAL); 99 append(AudioDeviceInfo.TYPE_DOCK, Spatialization.Mode.TRANSAURAL); 100 append(AudioDeviceInfo.TYPE_USB_ACCESSORY, Spatialization.Mode.TRANSAURAL); 101 append(AudioDeviceInfo.TYPE_USB_DEVICE, Spatialization.Mode.TRANSAURAL); 102 append(AudioDeviceInfo.TYPE_USB_HEADSET, Spatialization.Mode.BINAURAL); 103 append(AudioDeviceInfo.TYPE_LINE_ANALOG, Spatialization.Mode.TRANSAURAL); 104 append(AudioDeviceInfo.TYPE_LINE_DIGITAL, Spatialization.Mode.TRANSAURAL); 105 append(AudioDeviceInfo.TYPE_AUX_LINE, Spatialization.Mode.TRANSAURAL); 106 append(AudioDeviceInfo.TYPE_BLE_HEADSET, Spatialization.Mode.BINAURAL); 107 append(AudioDeviceInfo.TYPE_BLE_SPEAKER, Spatialization.Mode.TRANSAURAL); 108 // assumption that BLE broadcast would be mostly consumed on headsets 109 append(AudioDeviceInfo.TYPE_BLE_BROADCAST, Spatialization.Mode.BINAURAL); 110 } 111 }; 112 113 // Spatializer state machine 114 /*package*/ static final int STATE_UNINITIALIZED = 0; 115 /*package*/ static final int STATE_NOT_SUPPORTED = 1; 116 /*package*/ static final int STATE_DISABLED_UNAVAILABLE = 3; 117 /*package*/ static final int STATE_ENABLED_UNAVAILABLE = 4; 118 /*package*/ static final int STATE_ENABLED_AVAILABLE = 5; 119 /*package*/ static final int STATE_DISABLED_AVAILABLE = 6; 120 private int mState = STATE_UNINITIALIZED; 121 122 @VisibleForTesting boolean mBinauralEnabledDefault; 123 @VisibleForTesting boolean mTransauralEnabledDefault; 124 @VisibleForTesting boolean mHeadTrackingEnabledDefault; 125 126 private boolean mFeatureEnabled = false; 127 /** current level as reported by native Spatializer in callback */ 128 private int mSpatLevel = Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE; 129 private int mCapableSpatLevel = Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE; 130 131 private boolean mTransauralSupported = false; 132 private boolean mBinauralSupported = false; 133 private boolean mIsHeadTrackingSupported = false; 134 private int[] mSupportedHeadTrackingModes = new int[0]; 135 private int mActualHeadTrackingMode = Spatializer.HEAD_TRACKING_MODE_UNSUPPORTED; 136 private int mDesiredHeadTrackingMode = Spatializer.HEAD_TRACKING_MODE_RELATIVE_WORLD; 137 private boolean mHeadTrackerAvailable = false; 138 /** 139 * The desired head tracking mode when enabling head tracking, tracks mDesiredHeadTrackingMode, 140 * except when head tracking gets disabled through setting the desired mode to 141 * {@link Spatializer#HEAD_TRACKING_MODE_DISABLED}. 142 */ 143 private int mDesiredHeadTrackingModeWhenEnabled = Spatializer.HEAD_TRACKING_MODE_RELATIVE_WORLD; 144 private int mSpatOutput = 0; 145 private @Nullable ISpatializer mSpat; 146 private @Nullable SpatializerCallback mSpatCallback; 147 private @Nullable SpatializerHeadTrackingCallback mSpatHeadTrackingCallback = 148 new SpatializerHeadTrackingCallback(); 149 private @Nullable HelperDynamicSensorCallback mDynSensorCallback; 150 151 // default attributes and format that determine basic availability of spatialization 152 private static final AudioAttributes DEFAULT_ATTRIBUTES = new AudioAttributes.Builder() 153 .setUsage(AudioAttributes.USAGE_MEDIA) 154 .build(); 155 private static final AudioFormat DEFAULT_FORMAT = new AudioFormat.Builder() 156 .setEncoding(AudioFormat.ENCODING_PCM_16BIT) 157 .setSampleRate(48000) 158 .setChannelMask(AudioFormat.CHANNEL_OUT_5POINT1) 159 .build(); 160 // device array to store the routing for the default attributes and format, initialized to 161 // an empty list as routing hasn't been established yet 162 private static ArrayList<AudioDeviceAttributes> sRoutingDevices = new ArrayList<>(0); 163 164 //--------------------------------------------------------------- 165 // audio device compatibility / enabled 166 /** 167 * List of device types that can be used on this device with Spatial Audio. 168 * It is initialized based on the transaural/binaural capabilities 169 * of the effect. 170 */ 171 private final ArrayList<Integer> mSACapableDeviceTypes = new ArrayList<>(0); 172 173 //------------------------------------------------------ 174 // initialization SpatializerHelper(@onNull AudioService mother, @NonNull AudioSystemAdapter asa, @NonNull AudioDeviceBroker deviceBroker, boolean binauralEnabledDefault, boolean transauralEnabledDefault, boolean headTrackingEnabledDefault)175 SpatializerHelper(@NonNull AudioService mother, @NonNull AudioSystemAdapter asa, 176 @NonNull AudioDeviceBroker deviceBroker, boolean binauralEnabledDefault, 177 boolean transauralEnabledDefault, boolean headTrackingEnabledDefault) { 178 mAudioService = mother; 179 mASA = asa; 180 mDeviceBroker = deviceBroker; 181 182 mBinauralEnabledDefault = binauralEnabledDefault; 183 mTransauralEnabledDefault = transauralEnabledDefault; 184 mHeadTrackingEnabledDefault = headTrackingEnabledDefault; 185 } 186 init(boolean effectExpected)187 synchronized void init(boolean effectExpected) { 188 loglogi("init effectExpected=" + effectExpected); 189 if (!effectExpected) { 190 loglogi("init(): setting state to STATE_NOT_SUPPORTED due to effect not expected"); 191 mState = STATE_NOT_SUPPORTED; 192 return; 193 } 194 if (mState != STATE_UNINITIALIZED) { 195 throw new IllegalStateException(logloge("init() called in state " + mState)); 196 } 197 // is there a spatializer? 198 mSpatCallback = new SpatializerCallback(); 199 final ISpatializer spat = AudioSystem.getSpatializer(mSpatCallback); 200 if (spat == null) { 201 loglogi("init(): No Spatializer found"); 202 mState = STATE_NOT_SUPPORTED; 203 return; 204 } 205 // capabilities of spatializer? 206 resetCapabilities(); 207 208 try { 209 byte[] levels = spat.getSupportedLevels(); 210 if (levels == null 211 || levels.length == 0 212 || (levels.length == 1 213 && levels[0] == Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE)) { 214 logloge("init(): found Spatializer is useless"); 215 mState = STATE_NOT_SUPPORTED; 216 return; 217 } 218 for (byte level : levels) { 219 loglogi("init(): found support for level: " + level); 220 if (level == Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_MULTICHANNEL) { 221 loglogi("init(): setting capable level to LEVEL_MULTICHANNEL"); 222 mCapableSpatLevel = level; 223 break; 224 } 225 } 226 227 // Note: head tracking support must be initialized before spatialization modes as 228 // addCompatibleAudioDevice() calls onRoutingUpdated() which will initialize the 229 // sensors according to mIsHeadTrackingSupported. 230 mIsHeadTrackingSupported = spat.isHeadTrackingSupported(); 231 if (mIsHeadTrackingSupported) { 232 final byte[] values = spat.getSupportedHeadTrackingModes(); 233 ArrayList<Integer> list = new ArrayList<>(0); 234 for (byte value : values) { 235 switch (value) { 236 case HeadTracking.Mode.OTHER: 237 case HeadTracking.Mode.DISABLED: 238 // not expected here, skip 239 break; 240 case HeadTracking.Mode.RELATIVE_WORLD: 241 case HeadTracking.Mode.RELATIVE_SCREEN: 242 list.add(headTrackingModeTypeToSpatializerInt(value)); 243 break; 244 default: 245 Log.e(TAG, "Unexpected head tracking mode:" + value, 246 new IllegalArgumentException("invalid mode")); 247 break; 248 } 249 } 250 mSupportedHeadTrackingModes = new int[list.size()]; 251 for (int i = 0; i < list.size(); i++) { 252 mSupportedHeadTrackingModes[i] = list.get(i); 253 } 254 mActualHeadTrackingMode = 255 headTrackingModeTypeToSpatializerInt(spat.getActualHeadTrackingMode()); 256 } else { 257 mDesiredHeadTrackingModeWhenEnabled = Spatializer.HEAD_TRACKING_MODE_UNSUPPORTED; 258 mDesiredHeadTrackingMode = Spatializer.HEAD_TRACKING_MODE_UNSUPPORTED; 259 } 260 261 byte[] spatModes = spat.getSupportedModes(); 262 for (byte mode : spatModes) { 263 switch (mode) { 264 case Spatialization.Mode.BINAURAL: 265 mBinauralSupported = true; 266 break; 267 case Spatialization.Mode.TRANSAURAL: 268 mTransauralSupported = true; 269 break; 270 default: 271 logloge("init(): Spatializer reports unknown supported mode:" + mode); 272 break; 273 } 274 } 275 // if neither transaural nor binaural is supported, bail 276 if (!mBinauralSupported && !mTransauralSupported) { 277 mState = STATE_NOT_SUPPORTED; 278 return; 279 } 280 281 // initialize list of compatible devices 282 for (int i = 0; i < SPAT_MODE_FOR_DEVICE_TYPE.size(); i++) { 283 int mode = SPAT_MODE_FOR_DEVICE_TYPE.valueAt(i); 284 if ((mode == (int) Spatialization.Mode.BINAURAL && mBinauralSupported) 285 || (mode == (int) Spatialization.Mode.TRANSAURAL 286 && mTransauralSupported)) { 287 mSACapableDeviceTypes.add(SPAT_MODE_FOR_DEVICE_TYPE.keyAt(i)); 288 } 289 } 290 291 // Log the saved device states that are compatible with SA 292 for (AdiDeviceState deviceState : mDeviceBroker.getImmutableDeviceInventory()) { 293 if (isSADevice(deviceState)) { 294 logDeviceState(deviceState, "setSADeviceSettings"); 295 } 296 } 297 298 // for both transaural / binaural, we are not forcing enablement as the init() method 299 // could have been called another time after boot in case of audioserver restart 300 addCompatibleAudioDevice( 301 new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_SPEAKER, ""), 302 false /*forceEnable*/, false /*forceInit*/); 303 // not force-enabling as this device might already be in the device list 304 addCompatibleAudioDevice( 305 new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE, ""), 306 false /*forceEnable*/, false /*forceInit*/); 307 } catch (RemoteException e) { 308 resetCapabilities(); 309 } finally { 310 if (spat != null) { 311 try { 312 spat.release(); 313 } catch (RemoteException e) { /* capable level remains at NONE*/ } 314 } 315 } 316 if (mCapableSpatLevel == Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE) { 317 mState = STATE_NOT_SUPPORTED; 318 return; 319 } 320 mState = STATE_DISABLED_UNAVAILABLE; 321 sRoutingDevices = getRoutingDevices(DEFAULT_ATTRIBUTES); 322 // note at this point mSpat is still not instantiated 323 } 324 325 /** 326 * Like init() but resets the state and spatializer levels 327 * @param featureEnabled 328 */ reset(boolean featureEnabled)329 synchronized void reset(boolean featureEnabled) { 330 loglogi("Resetting featureEnabled=" + featureEnabled); 331 releaseSpat(); 332 mState = STATE_UNINITIALIZED; 333 mSpatLevel = Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE; 334 mActualHeadTrackingMode = Spatializer.HEAD_TRACKING_MODE_UNSUPPORTED; 335 init(/*effectExpected=*/true); 336 setSpatializerEnabledInt(featureEnabled); 337 } 338 resetCapabilities()339 private void resetCapabilities() { 340 mCapableSpatLevel = Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE; 341 mBinauralSupported = false; 342 mTransauralSupported = false; 343 mIsHeadTrackingSupported = false; 344 mSupportedHeadTrackingModes = new int[0]; 345 } 346 347 //------------------------------------------------------ 348 // routing monitoring onRoutingUpdated()349 synchronized void onRoutingUpdated() { 350 switch (mState) { 351 case STATE_UNINITIALIZED: 352 case STATE_NOT_SUPPORTED: 353 return; 354 case STATE_DISABLED_UNAVAILABLE: 355 case STATE_ENABLED_UNAVAILABLE: 356 case STATE_ENABLED_AVAILABLE: 357 case STATE_DISABLED_AVAILABLE: 358 break; 359 } 360 361 sRoutingDevices = getRoutingDevices(DEFAULT_ATTRIBUTES); 362 363 // check validity of routing information 364 if (sRoutingDevices.isEmpty()) { 365 logloge("onRoutingUpdated: no device, no Spatial Audio"); 366 setDispatchAvailableState(false); 367 // not changing the spatializer level as this is likely a transient state 368 return; 369 } 370 final AudioDeviceAttributes currentDevice = sRoutingDevices.get(0); 371 372 // is media routed to a new device? 373 if (isBluetoothDevice(currentDevice.getInternalType())) { 374 addWirelessDeviceIfNew(currentDevice); 375 } 376 377 // find if media device enabled / available 378 final Pair<Boolean, Boolean> enabledAvailable = evaluateState(currentDevice); 379 380 boolean able = false; 381 if (enabledAvailable.second) { 382 // available for Spatial audio, check w/ effect 383 able = canBeSpatializedOnDevice(DEFAULT_ATTRIBUTES, DEFAULT_FORMAT, sRoutingDevices); 384 loglogi("onRoutingUpdated: can spatialize media 5.1:" + able 385 + " on device:" + currentDevice); 386 setDispatchAvailableState(able); 387 } else { 388 loglogi("onRoutingUpdated: device:" + currentDevice 389 + " not available for Spatial Audio"); 390 setDispatchAvailableState(false); 391 } 392 393 boolean enabled = mFeatureEnabled && able && enabledAvailable.first; 394 if (enabled) { 395 loglogi("Enabling Spatial Audio since enabled for media device:" 396 + currentDevice); 397 } else { 398 loglogi("Disabling Spatial Audio since disabled for media device:" 399 + currentDevice); 400 } 401 if (mSpat != null) { 402 byte level = enabled ? (byte) Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_MULTICHANNEL 403 : (byte) Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE; 404 loglogi("Setting spatialization level to: " + level); 405 try { 406 mSpat.setLevel(level); 407 } catch (RemoteException e) { 408 Log.e(TAG, "onRoutingUpdated() Can't set spatializer level", e); 409 // try to recover by resetting the native spatializer state 410 postReset(); 411 return; 412 } 413 } 414 415 setDispatchFeatureEnabledState(enabled, "onRoutingUpdated"); 416 417 if (mDesiredHeadTrackingMode != Spatializer.HEAD_TRACKING_MODE_UNSUPPORTED 418 && mDesiredHeadTrackingMode != Spatializer.HEAD_TRACKING_MODE_DISABLED) { 419 postInitSensors(); 420 } 421 } 422 postReset()423 private void postReset() { 424 mAudioService.postResetSpatializer(); 425 } 426 427 //------------------------------------------------------ 428 // spatializer callback from native 429 private final class SpatializerCallback extends INativeSpatializerCallback.Stub { 430 onLevelChanged(byte level)431 public void onLevelChanged(byte level) { 432 loglogi("SpatializerCallback.onLevelChanged level:" + level); 433 synchronized (SpatializerHelper.this) { 434 mSpatLevel = spatializationLevelToSpatializerInt(level); 435 } 436 // TODO use reported spat level to change state 437 438 // init sensors 439 postInitSensors(); 440 } 441 onOutputChanged(int output)442 public void onOutputChanged(int output) { 443 loglogi("SpatializerCallback.onOutputChanged output:" + output); 444 int oldOutput; 445 synchronized (SpatializerHelper.this) { 446 oldOutput = mSpatOutput; 447 mSpatOutput = output; 448 } 449 if (oldOutput != output) { 450 dispatchOutputUpdate(output); 451 } 452 } 453 }; 454 455 //------------------------------------------------------ 456 // spatializer head tracking callback from native 457 private final class SpatializerHeadTrackingCallback 458 extends ISpatializerHeadTrackingCallback.Stub { onHeadTrackingModeChanged(byte mode)459 public void onHeadTrackingModeChanged(byte mode) { 460 int oldMode, newMode; 461 synchronized (this) { 462 oldMode = mActualHeadTrackingMode; 463 mActualHeadTrackingMode = headTrackingModeTypeToSpatializerInt(mode); 464 newMode = mActualHeadTrackingMode; 465 } 466 loglogi("SpatializerHeadTrackingCallback.onHeadTrackingModeChanged mode:" 467 + Spatializer.headtrackingModeToString(newMode)); 468 if (oldMode != newMode) { 469 dispatchActualHeadTrackingMode(newMode); 470 } 471 } 472 onHeadToSoundStagePoseUpdated(float[] headToStage)473 public void onHeadToSoundStagePoseUpdated(float[] headToStage) { 474 if (headToStage == null) { 475 Log.e(TAG, "SpatializerHeadTrackingCallback.onHeadToStagePoseUpdated" 476 + "null transform"); 477 return; 478 } 479 if (headToStage.length != 6) { 480 Log.e(TAG, "SpatializerHeadTrackingCallback.onHeadToStagePoseUpdated" 481 + " invalid transform length" + headToStage.length); 482 return; 483 } 484 if (DEBUG_MORE) { 485 // 6 values * (4 digits + 1 dot + 2 brackets) = 42 characters 486 StringBuilder t = new StringBuilder(42); 487 for (float val : headToStage) { 488 t.append("[").append(String.format(Locale.ENGLISH, "%.3f", val)).append("]"); 489 } 490 loglogi("SpatializerHeadTrackingCallback.onHeadToStagePoseUpdated headToStage:" 491 + t); 492 } 493 dispatchPoseUpdate(headToStage); 494 } 495 }; 496 497 //------------------------------------------------------ 498 // dynamic sensor callback 499 private final class HelperDynamicSensorCallback extends SensorManager.DynamicSensorCallback { 500 @Override onDynamicSensorConnected(Sensor sensor)501 public void onDynamicSensorConnected(Sensor sensor) { 502 postInitSensors(); 503 } 504 505 @Override onDynamicSensorDisconnected(Sensor sensor)506 public void onDynamicSensorDisconnected(Sensor sensor) { 507 postInitSensors(); 508 } 509 } 510 511 //------------------------------------------------------ 512 // compatible devices 513 /** 514 * Return the list of compatible devices, which reflects the device compatible with the 515 * spatializer effect, and those that have been explicitly enabled or disabled 516 * @return the list of compatible audio devices 517 */ getCompatibleAudioDevices()518 synchronized @NonNull List<AudioDeviceAttributes> getCompatibleAudioDevices() { 519 // build unionOf(mCompatibleAudioDevices, mEnabledDevice) - mDisabledAudioDevices 520 ArrayList<AudioDeviceAttributes> compatList = new ArrayList<>(); 521 for (AdiDeviceState deviceState : mDeviceBroker.getImmutableDeviceInventory()) { 522 if (deviceState.isSAEnabled() && isSADevice(deviceState)) { 523 compatList.add(deviceState.getAudioDeviceAttributes()); 524 } 525 } 526 return compatList; 527 } 528 addCompatibleAudioDevice(@onNull AudioDeviceAttributes ada)529 synchronized void addCompatibleAudioDevice(@NonNull AudioDeviceAttributes ada) { 530 addCompatibleAudioDevice(ada, true /*forceEnable*/, false /*forceInit*/); 531 } 532 533 /** 534 * Add the given device to the list of devices for which spatial audio will be available 535 * (== possible). 536 * @param ada the compatible device 537 * @param forceEnable if true, spatial audio is enabled for this device, regardless of whether 538 * this device was already in the list. If false, the enabled field is only 539 * set to true if the device is added to the list, otherwise, if already 540 * present, the setting is left untouched. 541 */ 542 @GuardedBy("this") addCompatibleAudioDevice(@onNull AudioDeviceAttributes ada, boolean forceEnable, boolean forceInit)543 private void addCompatibleAudioDevice(@NonNull AudioDeviceAttributes ada, 544 boolean forceEnable, boolean forceInit) { 545 if (!isDeviceCompatibleWithSpatializationModes(ada)) { 546 return; 547 } 548 loglogi("addCompatibleAudioDevice: dev=" + ada); 549 final AdiDeviceState deviceState = findSACompatibleDeviceStateForAudioDeviceAttributes(ada); 550 AdiDeviceState updatedDevice = null; // non-null on update. 551 if (deviceState != null) { 552 if (forceInit) { 553 initSAState(deviceState); 554 } 555 if (forceEnable && !deviceState.isSAEnabled()) { 556 updatedDevice = deviceState; 557 updatedDevice.setSAEnabled(true); 558 } 559 } else { 560 // When adding, force the device type to be a canonical one. 561 final int canonicalDeviceType = getCanonicalDeviceType(ada.getType(), 562 ada.getInternalType()); 563 if (canonicalDeviceType == AudioDeviceInfo.TYPE_UNKNOWN) { 564 Log.e(TAG, "addCompatibleAudioDevice with incompatible AudioDeviceAttributes " 565 + ada); 566 return; 567 } 568 updatedDevice = new AdiDeviceState(canonicalDeviceType, ada.getInternalType(), 569 ada.getAddress()); 570 initSAState(updatedDevice); 571 mDeviceBroker.addOrUpdateDeviceSAStateInInventory( 572 updatedDevice, true /*syncInventory*/); 573 } 574 if (updatedDevice != null) { 575 onRoutingUpdated(); 576 mDeviceBroker.postPersistAudioDeviceSettings(); 577 logDeviceState(updatedDevice, "addCompatibleAudioDevice"); 578 } 579 } 580 initSAState(AdiDeviceState device)581 private void initSAState(AdiDeviceState device) { 582 if (device == null) { 583 return; 584 } 585 586 int spatMode = SPAT_MODE_FOR_DEVICE_TYPE.get(device.getDeviceType(), 587 Integer.MIN_VALUE); 588 device.setSAEnabled(spatMode == Spatialization.Mode.BINAURAL 589 ? mBinauralEnabledDefault 590 : spatMode == Spatialization.Mode.TRANSAURAL 591 ? mTransauralEnabledDefault 592 : false); 593 device.setHeadTrackerEnabled(mHeadTrackingEnabledDefault); 594 } 595 596 private static final String METRICS_DEVICE_PREFIX = "audio.spatializer.device."; 597 598 // Device logging is accomplished in the Java Audio Service level. 599 // (System capabilities is done in the Native AudioPolicyManager level). 600 // 601 // There may be different devices with the same device type (aliasing). 602 // We always send the full device state info on each change. logDeviceState(AdiDeviceState deviceState, String event)603 static void logDeviceState(AdiDeviceState deviceState, String event) { 604 final int deviceType = AudioDeviceInfo.convertDeviceTypeToInternalDevice( 605 deviceState.getDeviceType()); 606 final String deviceName = AudioSystem.getDeviceName(deviceType); 607 new MediaMetrics.Item(METRICS_DEVICE_PREFIX + deviceName) 608 .set(MediaMetrics.Property.ADDRESS, deviceState.getDeviceAddress()) 609 .set(MediaMetrics.Property.ENABLED, deviceState.isSAEnabled() ? "true" : "false") 610 .set(MediaMetrics.Property.EVENT, TextUtils.emptyIfNull(event)) 611 .set(MediaMetrics.Property.HAS_HEAD_TRACKER, 612 deviceState.hasHeadTracker() ? "true" 613 : "false") // this may be updated later. 614 .set(MediaMetrics.Property.HEAD_TRACKER_ENABLED, 615 deviceState.isHeadTrackerEnabled() ? "true" : "false") 616 .record(); 617 } 618 removeCompatibleAudioDevice(@onNull AudioDeviceAttributes ada)619 synchronized void removeCompatibleAudioDevice(@NonNull AudioDeviceAttributes ada) { 620 loglogi("removeCompatibleAudioDevice: dev=" + ada); 621 622 final AdiDeviceState deviceState = findSACompatibleDeviceStateForAudioDeviceAttributes(ada); 623 if (deviceState != null && deviceState.isSAEnabled()) { 624 deviceState.setSAEnabled(false); 625 onRoutingUpdated(); 626 mDeviceBroker.postPersistAudioDeviceSettings(); 627 logDeviceState(deviceState, "removeCompatibleAudioDevice"); 628 } 629 } 630 631 /** 632 * Returns a possibly aliased device type which is used 633 * for spatial audio settings (or TYPE_UNKNOWN if it doesn't exist). 634 */ 635 @AudioDeviceInfo.AudioDeviceType getCanonicalDeviceType(int deviceType, int internalDeviceType)636 private static int getCanonicalDeviceType(int deviceType, int internalDeviceType) { 637 if (isBluetoothDevice(internalDeviceType)) return deviceType; 638 639 final int spatMode = SPAT_MODE_FOR_DEVICE_TYPE.get(deviceType, Integer.MIN_VALUE); 640 if (spatMode == Spatialization.Mode.TRANSAURAL) { 641 return AudioDeviceInfo.TYPE_BUILTIN_SPEAKER; 642 } else if (spatMode == Spatialization.Mode.BINAURAL) { 643 return AudioDeviceInfo.TYPE_WIRED_HEADPHONES; 644 } 645 return AudioDeviceInfo.TYPE_UNKNOWN; 646 } 647 648 /** 649 * Returns the audio device state for the audio device attributes in case 650 * spatial audio is supported or null otherwise. 651 */ 652 @GuardedBy("this") 653 @Nullable findSACompatibleDeviceStateForAudioDeviceAttributes( AudioDeviceAttributes ada)654 private AdiDeviceState findSACompatibleDeviceStateForAudioDeviceAttributes( 655 AudioDeviceAttributes ada) { 656 final AdiDeviceState deviceState = 657 mDeviceBroker.findDeviceStateForAudioDeviceAttributes(ada, 658 getCanonicalDeviceType(ada.getType(), ada.getInternalType())); 659 if (deviceState == null) { 660 return null; 661 } 662 663 if (!isSADevice(deviceState)) { 664 return null; 665 } 666 667 return deviceState; 668 } 669 670 /** 671 * Return if Spatial Audio is enabled and available for the given device 672 * @param ada 673 * @return a pair of boolean, 1/ enabled? 2/ available? 674 */ evaluateState(AudioDeviceAttributes ada)675 private synchronized Pair<Boolean, Boolean> evaluateState(AudioDeviceAttributes ada) { 676 final @AudioDeviceInfo.AudioDeviceType int deviceType = ada.getType(); 677 // is the device type capable of doing SA? 678 if (!mSACapableDeviceTypes.contains(deviceType)) { 679 Log.i(TAG, "Device incompatible with Spatial Audio dev:" + ada); 680 return new Pair<>(false, false); 681 } 682 // what spatialization mode to use for this device? 683 final int spatMode = SPAT_MODE_FOR_DEVICE_TYPE.get(deviceType, Integer.MIN_VALUE); 684 if (spatMode == Integer.MIN_VALUE) { 685 // error case, device not found 686 Log.e(TAG, "no spatialization mode found for device type:" + deviceType); 687 return new Pair<>(false, false); 688 } 689 final AdiDeviceState deviceState = findSACompatibleDeviceStateForAudioDeviceAttributes(ada); 690 if (deviceState == null) { 691 // no matching device state? 692 Log.i(TAG, "no spatialization device state found for Spatial Audio device:" + ada); 693 return new Pair<>(false, false); 694 } 695 boolean available = true; 696 if (isBluetoothDevice(deviceType)) { 697 // only checking headphones/binaural because external speakers cannot use transaural 698 // since their physical characteristics are unknown 699 if (deviceState.getAudioDeviceCategory() == AUDIO_DEVICE_CATEGORY_UNKNOWN 700 || deviceState.getAudioDeviceCategory() == AUDIO_DEVICE_CATEGORY_HEADPHONES) { 701 available = (spatMode == Spatialization.Mode.BINAURAL) && mBinauralSupported; 702 } else { 703 available = false; 704 } 705 } 706 // found the matching device state. 707 return new Pair<>(deviceState.isSAEnabled(), available); 708 } 709 addWirelessDeviceIfNew(@onNull AudioDeviceAttributes ada)710 private synchronized void addWirelessDeviceIfNew(@NonNull AudioDeviceAttributes ada) { 711 if (!isDeviceCompatibleWithSpatializationModes(ada)) { 712 return; 713 } 714 if (findSACompatibleDeviceStateForAudioDeviceAttributes(ada) == null) { 715 // wireless device types should be canonical, but we translate to be sure. 716 final int canonicalDeviceType = getCanonicalDeviceType(ada.getType(), 717 ada.getInternalType()); 718 if (canonicalDeviceType == AudioDeviceInfo.TYPE_UNKNOWN) { 719 Log.e(TAG, "addWirelessDeviceIfNew with incompatible AudioDeviceAttributes " 720 + ada); 721 return; 722 } 723 final AdiDeviceState deviceState = 724 new AdiDeviceState(canonicalDeviceType, ada.getInternalType(), 725 ada.getAddress()); 726 initSAState(deviceState); 727 mDeviceBroker.addOrUpdateDeviceSAStateInInventory(deviceState, true /*syncInventory*/); 728 mDeviceBroker.postPersistAudioDeviceSettings(); 729 logDeviceState(deviceState, "addWirelessDeviceIfNew"); // may be updated later. 730 } 731 } 732 733 //------------------------------------------------------ 734 // states 735 isEnabled()736 synchronized boolean isEnabled() { 737 switch (mState) { 738 case STATE_UNINITIALIZED: 739 case STATE_NOT_SUPPORTED: 740 case STATE_DISABLED_UNAVAILABLE: 741 case STATE_DISABLED_AVAILABLE: 742 return false; 743 case STATE_ENABLED_UNAVAILABLE: 744 case STATE_ENABLED_AVAILABLE: 745 default: 746 return true; 747 } 748 } 749 isAvailable()750 synchronized boolean isAvailable() { 751 switch (mState) { 752 case STATE_UNINITIALIZED: 753 case STATE_NOT_SUPPORTED: 754 case STATE_ENABLED_UNAVAILABLE: 755 case STATE_DISABLED_UNAVAILABLE: 756 return false; 757 case STATE_DISABLED_AVAILABLE: 758 case STATE_ENABLED_AVAILABLE: 759 default: 760 return true; 761 } 762 } 763 refreshDevice(@onNull AudioDeviceAttributes ada, boolean initState)764 synchronized void refreshDevice(@NonNull AudioDeviceAttributes ada, boolean initState) { 765 final AdiDeviceState deviceState = findSACompatibleDeviceStateForAudioDeviceAttributes(ada); 766 if (isAvailableForAdiDeviceState(deviceState)) { 767 addCompatibleAudioDevice(ada, /*forceEnable=*/deviceState.isSAEnabled(), initState); 768 setHeadTrackerEnabled(deviceState.isHeadTrackerEnabled(), ada); 769 } else { 770 removeCompatibleAudioDevice(ada); 771 } 772 } 773 isAvailableForDevice(@onNull AudioDeviceAttributes ada)774 synchronized boolean isAvailableForDevice(@NonNull AudioDeviceAttributes ada) { 775 if (ada.getRole() != AudioDeviceAttributes.ROLE_OUTPUT) { 776 return false; 777 } 778 779 return isAvailableForAdiDeviceState( 780 findSACompatibleDeviceStateForAudioDeviceAttributes(ada)); 781 } 782 isAvailableForAdiDeviceState(AdiDeviceState deviceState)783 private boolean isAvailableForAdiDeviceState(AdiDeviceState deviceState) { 784 if (deviceState == null) { 785 return false; 786 } 787 788 if (isBluetoothDevice(deviceState.getInternalDeviceType()) 789 && deviceState.getAudioDeviceCategory() != AUDIO_DEVICE_CATEGORY_UNKNOWN 790 && deviceState.getAudioDeviceCategory() != AUDIO_DEVICE_CATEGORY_HEADPHONES) { 791 return false; 792 } 793 return true; 794 } 795 canBeSpatializedOnDevice(@onNull AudioAttributes attributes, @NonNull AudioFormat format, @NonNull ArrayList<AudioDeviceAttributes> devices)796 private synchronized boolean canBeSpatializedOnDevice(@NonNull AudioAttributes attributes, 797 @NonNull AudioFormat format, @NonNull ArrayList<AudioDeviceAttributes> devices) { 798 if (devices.isEmpty()) { 799 return false; 800 } 801 if (isDeviceCompatibleWithSpatializationModes(devices.get(0))) { 802 AudioDeviceAttributes[] devArray = new AudioDeviceAttributes[devices.size()]; 803 return AudioSystem.canBeSpatialized(attributes, format, devices.toArray(devArray)); 804 } 805 return false; 806 } 807 isDeviceCompatibleWithSpatializationModes(@onNull AudioDeviceAttributes ada)808 private boolean isDeviceCompatibleWithSpatializationModes(@NonNull AudioDeviceAttributes ada) { 809 // modeForDevice will be neither transaural or binaural for devices that do not support 810 // spatial audio. For instance mono devices like earpiece or sco must 811 // not be included. 812 final byte modeForDevice = (byte) SPAT_MODE_FOR_DEVICE_TYPE.get(ada.getType(), 813 /*default when type not found*/ -1); 814 if ((modeForDevice == Spatialization.Mode.BINAURAL && mBinauralSupported) 815 || (modeForDevice == Spatialization.Mode.TRANSAURAL 816 && mTransauralSupported)) { 817 return true; 818 } 819 return false; 820 } 821 isSADevice(AdiDeviceState deviceState)822 /*package*/ boolean isSADevice(AdiDeviceState deviceState) { 823 return deviceState.getDeviceType() == getCanonicalDeviceType(deviceState.getDeviceType(), 824 deviceState.getInternalDeviceType()) && isDeviceCompatibleWithSpatializationModes( 825 deviceState.getAudioDeviceAttributes()); 826 } 827 setFeatureEnabled(boolean enabled)828 synchronized void setFeatureEnabled(boolean enabled) { 829 loglogi("setFeatureEnabled(" + enabled + ") was featureEnabled:" + mFeatureEnabled); 830 if (mFeatureEnabled == enabled) { 831 return; 832 } 833 mFeatureEnabled = enabled; 834 if (mFeatureEnabled) { 835 if (mState == STATE_NOT_SUPPORTED) { 836 Log.e(TAG, "Can't enabled Spatial Audio, unsupported"); 837 return; 838 } 839 if (mState == STATE_UNINITIALIZED) { 840 init(true); 841 } 842 setSpatializerEnabledInt(true); 843 } else { 844 setSpatializerEnabledInt(false); 845 } 846 } 847 setSpatializerEnabledInt(boolean enabled)848 synchronized void setSpatializerEnabledInt(boolean enabled) { 849 switch (mState) { 850 case STATE_UNINITIALIZED: 851 if (enabled) { 852 throw (new IllegalStateException("Can't enable when uninitialized")); 853 } 854 break; 855 case STATE_NOT_SUPPORTED: 856 if (enabled) { 857 Log.e(TAG, "Can't enable when unsupported"); 858 } 859 break; 860 case STATE_DISABLED_UNAVAILABLE: 861 case STATE_DISABLED_AVAILABLE: 862 if (enabled) { 863 createSpat(); 864 onRoutingUpdated(); 865 // onRoutingUpdated() can update the "enabled" state based on context 866 // and will call setDispatchFeatureEnabledState(). 867 } // else { nothing to do as already disabled } 868 break; 869 case STATE_ENABLED_UNAVAILABLE: 870 case STATE_ENABLED_AVAILABLE: 871 if (!enabled) { 872 releaseSpat(); 873 setDispatchFeatureEnabledState(false, "setSpatializerEnabledInt"); 874 } // else { nothing to do as already enabled } 875 break; 876 } 877 } 878 getCapableImmersiveAudioLevel()879 synchronized int getCapableImmersiveAudioLevel() { 880 return mCapableSpatLevel; 881 } 882 883 final RemoteCallbackList<ISpatializerCallback> mStateCallbacks = 884 new RemoteCallbackList<ISpatializerCallback>(); 885 registerStateCallback( @onNull ISpatializerCallback callback)886 synchronized void registerStateCallback( 887 @NonNull ISpatializerCallback callback) { 888 mStateCallbacks.register(callback); 889 } 890 unregisterStateCallback( @onNull ISpatializerCallback callback)891 synchronized void unregisterStateCallback( 892 @NonNull ISpatializerCallback callback) { 893 mStateCallbacks.unregister(callback); 894 } 895 896 /** 897 * Update the feature state, no-op if no change 898 * @param featureEnabled 899 */ setDispatchFeatureEnabledState(boolean featureEnabled, String source)900 private synchronized void setDispatchFeatureEnabledState(boolean featureEnabled, String source) 901 { 902 if (featureEnabled) { 903 switch (mState) { 904 case STATE_DISABLED_UNAVAILABLE: 905 mState = STATE_ENABLED_UNAVAILABLE; 906 break; 907 case STATE_DISABLED_AVAILABLE: 908 mState = STATE_ENABLED_AVAILABLE; 909 break; 910 case STATE_ENABLED_AVAILABLE: 911 case STATE_ENABLED_UNAVAILABLE: 912 // already enabled: no-op 913 loglogi("setDispatchFeatureEnabledState(" + featureEnabled 914 + ") no dispatch: mState:" 915 + spatStateString(mState) + " src:" + source); 916 return; 917 default: 918 throw (new IllegalStateException("Invalid mState:" + mState 919 + " for enabled true")); 920 } 921 } else { 922 switch (mState) { 923 case STATE_ENABLED_UNAVAILABLE: 924 mState = STATE_DISABLED_UNAVAILABLE; 925 break; 926 case STATE_ENABLED_AVAILABLE: 927 mState = STATE_DISABLED_AVAILABLE; 928 break; 929 case STATE_DISABLED_AVAILABLE: 930 case STATE_DISABLED_UNAVAILABLE: 931 // already disabled: no-op 932 loglogi("setDispatchFeatureEnabledState(" + featureEnabled 933 + ") no dispatch: mState:" + spatStateString(mState) 934 + " src:" + source); 935 return; 936 default: 937 throw (new IllegalStateException("Invalid mState:" + mState 938 + " for enabled false")); 939 } 940 } 941 loglogi("setDispatchFeatureEnabledState(" + featureEnabled 942 + ") mState:" + spatStateString(mState)); 943 final int nbCallbacks = mStateCallbacks.beginBroadcast(); 944 for (int i = 0; i < nbCallbacks; i++) { 945 try { 946 mStateCallbacks.getBroadcastItem(i) 947 .dispatchSpatializerEnabledChanged(featureEnabled); 948 } catch (RemoteException e) { 949 Log.e(TAG, "Error in dispatchSpatializerEnabledChanged", e); 950 } 951 } 952 mStateCallbacks.finishBroadcast(); 953 } 954 setDispatchAvailableState(boolean available)955 private synchronized void setDispatchAvailableState(boolean available) { 956 switch (mState) { 957 case STATE_UNINITIALIZED: 958 case STATE_NOT_SUPPORTED: 959 throw (new IllegalStateException( 960 "Should not update available state in state:" + mState)); 961 case STATE_DISABLED_UNAVAILABLE: 962 if (available) { 963 mState = STATE_DISABLED_AVAILABLE; 964 break; 965 } else { 966 // already in unavailable state 967 loglogi("setDispatchAvailableState(" + available 968 + ") no dispatch: mState:" + spatStateString(mState)); 969 return; 970 } 971 case STATE_ENABLED_UNAVAILABLE: 972 if (available) { 973 mState = STATE_ENABLED_AVAILABLE; 974 break; 975 } else { 976 // already in unavailable state 977 loglogi("setDispatchAvailableState(" + available 978 + ") no dispatch: mState:" + spatStateString(mState)); 979 return; 980 } 981 case STATE_DISABLED_AVAILABLE: 982 if (available) { 983 // already in available state 984 loglogi("setDispatchAvailableState(" + available 985 + ") no dispatch: mState:" + spatStateString(mState)); 986 return; 987 } else { 988 mState = STATE_DISABLED_UNAVAILABLE; 989 break; 990 } 991 case STATE_ENABLED_AVAILABLE: 992 if (available) { 993 // already in available state 994 loglogi("setDispatchAvailableState(" + available 995 + ") no dispatch: mState:" + spatStateString(mState)); 996 return; 997 } else { 998 mState = STATE_ENABLED_UNAVAILABLE; 999 break; 1000 } 1001 } 1002 loglogi("setDispatchAvailableState(" + available + ") mState:" + spatStateString(mState)); 1003 final int nbCallbacks = mStateCallbacks.beginBroadcast(); 1004 for (int i = 0; i < nbCallbacks; i++) { 1005 try { 1006 mStateCallbacks.getBroadcastItem(i) 1007 .dispatchSpatializerAvailableChanged(available); 1008 } catch (RemoteException e) { 1009 Log.e(TAG, "Error in dispatchSpatializerEnabledChanged", e); 1010 } 1011 } 1012 mStateCallbacks.finishBroadcast(); 1013 } 1014 1015 //------------------------------------------------------ 1016 // native Spatializer management 1017 1018 /** 1019 * precondition: mState == STATE_DISABLED_* 1020 */ createSpat()1021 private void createSpat() { 1022 if (mSpat == null) { 1023 mSpatCallback = new SpatializerCallback(); 1024 mSpat = AudioSystem.getSpatializer(mSpatCallback); 1025 if (mSpat == null) { 1026 Log.e(TAG, "createSpat(): No Spatializer found"); 1027 postReset(); 1028 return; 1029 } 1030 try { 1031 //TODO: register heatracking callback only when sensors are registered 1032 if (mIsHeadTrackingSupported) { 1033 mActualHeadTrackingMode = 1034 headTrackingModeTypeToSpatializerInt(mSpat.getActualHeadTrackingMode()); 1035 mSpat.registerHeadTrackingCallback(mSpatHeadTrackingCallback); 1036 } 1037 } catch (RemoteException e) { 1038 Log.e(TAG, "Can't configure head tracking", e); 1039 mState = STATE_NOT_SUPPORTED; 1040 mCapableSpatLevel = Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE; 1041 mActualHeadTrackingMode = Spatializer.HEAD_TRACKING_MODE_UNSUPPORTED; 1042 } 1043 } 1044 } 1045 1046 /** 1047 * precondition: mState == STATE_ENABLED_* 1048 */ releaseSpat()1049 private void releaseSpat() { 1050 if (mSpat != null) { 1051 mSpatCallback = null; 1052 try { 1053 if (mIsHeadTrackingSupported) { 1054 mSpat.registerHeadTrackingCallback(null); 1055 } 1056 mHeadTrackerAvailable = false; 1057 mSpat.release(); 1058 } catch (RemoteException e) { 1059 Log.e(TAG, "Can't set release spatializer cleanly", e); 1060 } 1061 mSpat = null; 1062 } 1063 } 1064 1065 //------------------------------------------------------ 1066 // virtualization capabilities canBeSpatialized( @onNull AudioAttributes attributes, @NonNull AudioFormat format)1067 synchronized boolean canBeSpatialized( 1068 @NonNull AudioAttributes attributes, @NonNull AudioFormat format) { 1069 switch (mState) { 1070 case STATE_UNINITIALIZED: 1071 case STATE_NOT_SUPPORTED: 1072 case STATE_ENABLED_UNAVAILABLE: 1073 case STATE_DISABLED_UNAVAILABLE: 1074 logd("canBeSpatialized false due to state:" + mState); 1075 return false; 1076 case STATE_DISABLED_AVAILABLE: 1077 case STATE_ENABLED_AVAILABLE: 1078 break; 1079 } 1080 1081 // filter on AudioAttributes usage 1082 switch (attributes.getUsage()) { 1083 case AudioAttributes.USAGE_MEDIA: 1084 case AudioAttributes.USAGE_GAME: 1085 break; 1086 default: 1087 logd("canBeSpatialized false due to usage:" + attributes.getUsage()); 1088 return false; 1089 } 1090 1091 // going through adapter to take advantage of routing cache 1092 final ArrayList<AudioDeviceAttributes> devices = getRoutingDevices(attributes); 1093 if (devices.isEmpty()) { 1094 logloge("canBeSpatialized got no device for " + attributes); 1095 return false; 1096 } 1097 final boolean able = canBeSpatializedOnDevice(attributes, format, devices); 1098 logd("canBeSpatialized usage:" + attributes.getUsage() 1099 + " format:" + format.toLogFriendlyString() + " returning " + able); 1100 return able; 1101 } 1102 1103 //------------------------------------------------------ 1104 // head tracking 1105 final RemoteCallbackList<ISpatializerHeadTrackingModeCallback> mHeadTrackingModeCallbacks = 1106 new RemoteCallbackList<ISpatializerHeadTrackingModeCallback>(); 1107 registerHeadTrackingModeCallback( @onNull ISpatializerHeadTrackingModeCallback callback)1108 synchronized void registerHeadTrackingModeCallback( 1109 @NonNull ISpatializerHeadTrackingModeCallback callback) { 1110 mHeadTrackingModeCallbacks.register(callback); 1111 } 1112 unregisterHeadTrackingModeCallback( @onNull ISpatializerHeadTrackingModeCallback callback)1113 synchronized void unregisterHeadTrackingModeCallback( 1114 @NonNull ISpatializerHeadTrackingModeCallback callback) { 1115 mHeadTrackingModeCallbacks.unregister(callback); 1116 } 1117 1118 final RemoteCallbackList<ISpatializerHeadTrackerAvailableCallback> mHeadTrackerCallbacks = 1119 new RemoteCallbackList<>(); 1120 registerHeadTrackerAvailableCallback( @onNull ISpatializerHeadTrackerAvailableCallback cb, boolean register)1121 synchronized void registerHeadTrackerAvailableCallback( 1122 @NonNull ISpatializerHeadTrackerAvailableCallback cb, boolean register) { 1123 if (register) { 1124 mHeadTrackerCallbacks.register(cb); 1125 } else { 1126 mHeadTrackerCallbacks.unregister(cb); 1127 } 1128 } 1129 getSupportedHeadTrackingModes()1130 synchronized int[] getSupportedHeadTrackingModes() { 1131 return mSupportedHeadTrackingModes; 1132 } 1133 getActualHeadTrackingMode()1134 synchronized int getActualHeadTrackingMode() { 1135 return mActualHeadTrackingMode; 1136 } 1137 getDesiredHeadTrackingMode()1138 synchronized int getDesiredHeadTrackingMode() { 1139 return mDesiredHeadTrackingMode; 1140 } 1141 setGlobalTransform(@onNull float[] transform)1142 synchronized void setGlobalTransform(@NonNull float[] transform) { 1143 if (transform.length != 6) { 1144 throw new IllegalArgumentException("invalid array size" + transform.length); 1145 } 1146 if (!checkSpatializerForHeadTracking("setGlobalTransform")) { 1147 return; 1148 } 1149 try { 1150 mSpat.setGlobalTransform(transform); 1151 } catch (RemoteException e) { 1152 Log.e(TAG, "Error calling setGlobalTransform", e); 1153 } 1154 } 1155 recenterHeadTracker()1156 synchronized void recenterHeadTracker() { 1157 if (!checkSpatializerForHeadTracking("recenterHeadTracker")) { 1158 return; 1159 } 1160 try { 1161 mSpat.recenterHeadTracker(); 1162 } catch (RemoteException e) { 1163 Log.e(TAG, "Error calling recenterHeadTracker", e); 1164 } 1165 } 1166 setDisplayOrientation(float displayOrientation)1167 synchronized void setDisplayOrientation(float displayOrientation) { 1168 if (!checkSpatializer("setDisplayOrientation")) { 1169 return; 1170 } 1171 try { 1172 mSpat.setDisplayOrientation(displayOrientation); 1173 } catch (RemoteException e) { 1174 Log.e(TAG, "Error calling setDisplayOrientation", e); 1175 } 1176 } 1177 setFoldState(boolean folded)1178 synchronized void setFoldState(boolean folded) { 1179 if (!checkSpatializer("setFoldState")) { 1180 return; 1181 } 1182 try { 1183 mSpat.setFoldState(folded); 1184 } catch (RemoteException e) { 1185 Log.e(TAG, "Error calling setFoldState", e); 1186 } 1187 } 1188 setDesiredHeadTrackingMode(@patializer.HeadTrackingModeSet int mode)1189 synchronized void setDesiredHeadTrackingMode(@Spatializer.HeadTrackingModeSet int mode) { 1190 if (!checkSpatializerForHeadTracking("setDesiredHeadTrackingMode")) { 1191 return; 1192 } 1193 if (mode != Spatializer.HEAD_TRACKING_MODE_DISABLED) { 1194 mDesiredHeadTrackingModeWhenEnabled = mode; 1195 } 1196 try { 1197 if (mDesiredHeadTrackingMode != mode) { 1198 mDesiredHeadTrackingMode = mode; 1199 dispatchDesiredHeadTrackingMode(mode); 1200 } 1201 Log.i(TAG, "setDesiredHeadTrackingMode(" 1202 + Spatializer.headtrackingModeToString(mode) + ")"); 1203 mSpat.setDesiredHeadTrackingMode(spatializerIntToHeadTrackingModeType(mode)); 1204 } catch (RemoteException e) { 1205 Log.e(TAG, "Error calling setDesiredHeadTrackingMode", e); 1206 } 1207 } 1208 setHeadTrackerEnabled(boolean enabled, @NonNull AudioDeviceAttributes ada)1209 synchronized void setHeadTrackerEnabled(boolean enabled, @NonNull AudioDeviceAttributes ada) { 1210 if (!mIsHeadTrackingSupported) { 1211 Log.v(TAG, "no headtracking support, ignoring setHeadTrackerEnabled to " + enabled 1212 + " for " + ada); 1213 } 1214 final AdiDeviceState deviceState = findSACompatibleDeviceStateForAudioDeviceAttributes(ada); 1215 if (deviceState == null) return; 1216 if (!deviceState.hasHeadTracker()) { 1217 Log.e(TAG, "Called setHeadTrackerEnabled enabled:" + enabled 1218 + " device:" + ada + " on a device without headtracker"); 1219 return; 1220 } 1221 Log.i(TAG, "setHeadTrackerEnabled enabled:" + enabled + " device:" + ada); 1222 deviceState.setHeadTrackerEnabled(enabled); 1223 mDeviceBroker.postPersistAudioDeviceSettings(); 1224 logDeviceState(deviceState, "setHeadTrackerEnabled"); 1225 1226 // check current routing to see if it affects the headtracking mode 1227 if (sRoutingDevices.isEmpty()) { 1228 logloge("setHeadTrackerEnabled: no device, bailing"); 1229 return; 1230 } 1231 final AudioDeviceAttributes currentDevice = sRoutingDevices.get(0); 1232 if (currentDevice.getType() == ada.getType() 1233 && currentDevice.getAddress().equals(ada.getAddress())) { 1234 setDesiredHeadTrackingMode(enabled ? mDesiredHeadTrackingModeWhenEnabled 1235 : Spatializer.HEAD_TRACKING_MODE_DISABLED); 1236 if (enabled && !mHeadTrackerAvailable) { 1237 postInitSensors(); 1238 } 1239 } 1240 } 1241 hasHeadTracker(@onNull AudioDeviceAttributes ada)1242 synchronized boolean hasHeadTracker(@NonNull AudioDeviceAttributes ada) { 1243 if (!mIsHeadTrackingSupported) { 1244 Log.v(TAG, "no headtracking support, hasHeadTracker always false for " + ada); 1245 return false; 1246 } 1247 final AdiDeviceState deviceState = findSACompatibleDeviceStateForAudioDeviceAttributes(ada); 1248 return deviceState != null && deviceState.hasHeadTracker(); 1249 } 1250 1251 /** 1252 * Configures device in list as having a head tracker 1253 * @param ada 1254 * @return true if the head tracker is enabled, false otherwise or if device not found 1255 */ setHasHeadTracker(@onNull AudioDeviceAttributes ada)1256 synchronized boolean setHasHeadTracker(@NonNull AudioDeviceAttributes ada) { 1257 if (!mIsHeadTrackingSupported) { 1258 Log.v(TAG, "no headtracking support, setHasHeadTracker always false for " + ada); 1259 return false; 1260 } 1261 final AdiDeviceState deviceState = findSACompatibleDeviceStateForAudioDeviceAttributes(ada); 1262 if (deviceState != null) { 1263 if (!deviceState.hasHeadTracker()) { 1264 deviceState.setHasHeadTracker(true); 1265 mDeviceBroker.postPersistAudioDeviceSettings(); 1266 logDeviceState(deviceState, "setHasHeadTracker"); 1267 } 1268 return deviceState.isHeadTrackerEnabled(); 1269 } 1270 Log.e(TAG, "setHasHeadTracker: device not found for:" + ada); 1271 return false; 1272 } 1273 isHeadTrackerEnabled(@onNull AudioDeviceAttributes ada)1274 synchronized boolean isHeadTrackerEnabled(@NonNull AudioDeviceAttributes ada) { 1275 if (!mIsHeadTrackingSupported) { 1276 Log.v(TAG, "no headtracking support, isHeadTrackerEnabled always false for " + ada); 1277 return false; 1278 } 1279 final AdiDeviceState deviceState = findSACompatibleDeviceStateForAudioDeviceAttributes(ada); 1280 return deviceState != null 1281 && deviceState.hasHeadTracker() && deviceState.isHeadTrackerEnabled(); 1282 } 1283 isHeadTrackerAvailable()1284 synchronized boolean isHeadTrackerAvailable() { 1285 return mHeadTrackerAvailable; 1286 } 1287 checkSpatializer(String funcName)1288 private boolean checkSpatializer(String funcName) { 1289 switch (mState) { 1290 case STATE_UNINITIALIZED: 1291 case STATE_NOT_SUPPORTED: 1292 return false; 1293 case STATE_ENABLED_UNAVAILABLE: 1294 case STATE_DISABLED_UNAVAILABLE: 1295 case STATE_DISABLED_AVAILABLE: 1296 case STATE_ENABLED_AVAILABLE: 1297 if (mSpat == null) { 1298 // try to recover by resetting the native spatializer state 1299 Log.e(TAG, "checkSpatializer(): called from " + funcName 1300 + "(), native spatializer should not be null in state: " + mState); 1301 postReset(); 1302 return false; 1303 } 1304 break; 1305 } 1306 return true; 1307 } 1308 checkSpatializerForHeadTracking(String funcName)1309 private boolean checkSpatializerForHeadTracking(String funcName) { 1310 return checkSpatializer(funcName) && mIsHeadTrackingSupported; 1311 } 1312 dispatchActualHeadTrackingMode(int newMode)1313 private void dispatchActualHeadTrackingMode(int newMode) { 1314 final int nbCallbacks = mHeadTrackingModeCallbacks.beginBroadcast(); 1315 for (int i = 0; i < nbCallbacks; i++) { 1316 try { 1317 mHeadTrackingModeCallbacks.getBroadcastItem(i) 1318 .dispatchSpatializerActualHeadTrackingModeChanged(newMode); 1319 } catch (RemoteException e) { 1320 Log.e(TAG, "Error in dispatchSpatializerActualHeadTrackingModeChanged(" 1321 + newMode + ")", e); 1322 } 1323 } 1324 mHeadTrackingModeCallbacks.finishBroadcast(); 1325 } 1326 dispatchDesiredHeadTrackingMode(int newMode)1327 private void dispatchDesiredHeadTrackingMode(int newMode) { 1328 final int nbCallbacks = mHeadTrackingModeCallbacks.beginBroadcast(); 1329 for (int i = 0; i < nbCallbacks; i++) { 1330 try { 1331 mHeadTrackingModeCallbacks.getBroadcastItem(i) 1332 .dispatchSpatializerDesiredHeadTrackingModeChanged(newMode); 1333 } catch (RemoteException e) { 1334 Log.e(TAG, "Error in dispatchSpatializerDesiredHeadTrackingModeChanged(" 1335 + newMode + ")", e); 1336 } 1337 } 1338 mHeadTrackingModeCallbacks.finishBroadcast(); 1339 } 1340 dispatchHeadTrackerAvailable(boolean available)1341 private void dispatchHeadTrackerAvailable(boolean available) { 1342 final int nbCallbacks = mHeadTrackerCallbacks.beginBroadcast(); 1343 for (int i = 0; i < nbCallbacks; i++) { 1344 try { 1345 mHeadTrackerCallbacks.getBroadcastItem(i) 1346 .dispatchSpatializerHeadTrackerAvailable(available); 1347 } catch (RemoteException e) { 1348 Log.e(TAG, "Error in dispatchSpatializerHeadTrackerAvailable(" 1349 + available + ")", e); 1350 } 1351 } 1352 mHeadTrackerCallbacks.finishBroadcast(); 1353 } 1354 1355 //------------------------------------------------------ 1356 // head pose 1357 final RemoteCallbackList<ISpatializerHeadToSoundStagePoseCallback> mHeadPoseCallbacks = 1358 new RemoteCallbackList<ISpatializerHeadToSoundStagePoseCallback>(); 1359 registerHeadToSoundstagePoseCallback( @onNull ISpatializerHeadToSoundStagePoseCallback callback)1360 synchronized void registerHeadToSoundstagePoseCallback( 1361 @NonNull ISpatializerHeadToSoundStagePoseCallback callback) { 1362 mHeadPoseCallbacks.register(callback); 1363 } 1364 unregisterHeadToSoundstagePoseCallback( @onNull ISpatializerHeadToSoundStagePoseCallback callback)1365 synchronized void unregisterHeadToSoundstagePoseCallback( 1366 @NonNull ISpatializerHeadToSoundStagePoseCallback callback) { 1367 mHeadPoseCallbacks.unregister(callback); 1368 } 1369 dispatchPoseUpdate(float[] pose)1370 private void dispatchPoseUpdate(float[] pose) { 1371 final int nbCallbacks = mHeadPoseCallbacks.beginBroadcast(); 1372 for (int i = 0; i < nbCallbacks; i++) { 1373 try { 1374 mHeadPoseCallbacks.getBroadcastItem(i) 1375 .dispatchPoseChanged(pose); 1376 } catch (RemoteException e) { 1377 Log.e(TAG, "Error in dispatchPoseChanged", e); 1378 } 1379 } 1380 mHeadPoseCallbacks.finishBroadcast(); 1381 } 1382 1383 //------------------------------------------------------ 1384 // vendor parameters setEffectParameter(int key, @NonNull byte[] value)1385 synchronized void setEffectParameter(int key, @NonNull byte[] value) { 1386 switch (mState) { 1387 case STATE_UNINITIALIZED: 1388 case STATE_NOT_SUPPORTED: 1389 throw (new IllegalStateException( 1390 "Can't set parameter key:" + key + " without a spatializer")); 1391 case STATE_ENABLED_UNAVAILABLE: 1392 case STATE_DISABLED_UNAVAILABLE: 1393 case STATE_DISABLED_AVAILABLE: 1394 case STATE_ENABLED_AVAILABLE: 1395 if (mSpat == null) { 1396 Log.e(TAG, "setParameter(" + key + "): null spatializer in state: " + mState); 1397 return; 1398 } 1399 break; 1400 } 1401 // mSpat != null 1402 try { 1403 mSpat.setParameter(key, value); 1404 } catch (RemoteException e) { 1405 Log.e(TAG, "Error in setParameter for key:" + key, e); 1406 } 1407 } 1408 getEffectParameter(int key, @NonNull byte[] value)1409 synchronized void getEffectParameter(int key, @NonNull byte[] value) { 1410 switch (mState) { 1411 case STATE_UNINITIALIZED: 1412 case STATE_NOT_SUPPORTED: 1413 throw (new IllegalStateException( 1414 "Can't get parameter key:" + key + " without a spatializer")); 1415 case STATE_ENABLED_UNAVAILABLE: 1416 case STATE_DISABLED_UNAVAILABLE: 1417 case STATE_DISABLED_AVAILABLE: 1418 case STATE_ENABLED_AVAILABLE: 1419 if (mSpat == null) { 1420 Log.e(TAG, "getParameter(" + key + "): null spatializer in state: " + mState); 1421 return; 1422 } 1423 break; 1424 } 1425 // mSpat != null 1426 try { 1427 mSpat.getParameter(key, value); 1428 } catch (RemoteException e) { 1429 Log.e(TAG, "Error in getParameter for key:" + key, e); 1430 } 1431 } 1432 1433 //------------------------------------------------------ 1434 // output 1435 1436 /** @see Spatializer#getOutput */ getOutput()1437 synchronized int getOutput() { 1438 switch (mState) { 1439 case STATE_UNINITIALIZED: 1440 case STATE_NOT_SUPPORTED: 1441 throw (new IllegalStateException( 1442 "Can't get output without a spatializer")); 1443 case STATE_ENABLED_UNAVAILABLE: 1444 case STATE_DISABLED_UNAVAILABLE: 1445 case STATE_DISABLED_AVAILABLE: 1446 case STATE_ENABLED_AVAILABLE: 1447 if (mSpat == null) { 1448 throw (new IllegalStateException( 1449 "null Spatializer for getOutput")); 1450 } 1451 break; 1452 } 1453 // mSpat != null 1454 try { 1455 return mSpat.getOutput(); 1456 } catch (RemoteException e) { 1457 Log.e(TAG, "Error in getOutput", e); 1458 return 0; 1459 } 1460 } 1461 1462 final RemoteCallbackList<ISpatializerOutputCallback> mOutputCallbacks = 1463 new RemoteCallbackList<ISpatializerOutputCallback>(); 1464 registerSpatializerOutputCallback( @onNull ISpatializerOutputCallback callback)1465 synchronized void registerSpatializerOutputCallback( 1466 @NonNull ISpatializerOutputCallback callback) { 1467 mOutputCallbacks.register(callback); 1468 } 1469 unregisterSpatializerOutputCallback( @onNull ISpatializerOutputCallback callback)1470 synchronized void unregisterSpatializerOutputCallback( 1471 @NonNull ISpatializerOutputCallback callback) { 1472 mOutputCallbacks.unregister(callback); 1473 } 1474 dispatchOutputUpdate(int output)1475 private void dispatchOutputUpdate(int output) { 1476 final int nbCallbacks = mOutputCallbacks.beginBroadcast(); 1477 for (int i = 0; i < nbCallbacks; i++) { 1478 try { 1479 mOutputCallbacks.getBroadcastItem(i).dispatchSpatializerOutputChanged(output); 1480 } catch (RemoteException e) { 1481 Log.e(TAG, "Error in dispatchOutputUpdate", e); 1482 } 1483 } 1484 mOutputCallbacks.finishBroadcast(); 1485 } 1486 1487 //------------------------------------------------------ 1488 // sensors postInitSensors()1489 private void postInitSensors() { 1490 mAudioService.postInitSpatializerHeadTrackingSensors(); 1491 } 1492 onInitSensors()1493 synchronized void onInitSensors() { 1494 final boolean init = mFeatureEnabled && (mSpatLevel != Spatialization.Level.NONE); 1495 final String action = init ? "initializing" : "releasing"; 1496 if (mSpat == null) { 1497 logloge("not " + action + " sensors, null spatializer"); 1498 return; 1499 } 1500 if (!mIsHeadTrackingSupported) { 1501 logloge("not " + action + " sensors, spatializer doesn't support headtracking"); 1502 return; 1503 } 1504 int headHandle = -1; 1505 int screenHandle = -1; 1506 if (init) { 1507 if (mSensorManager == null) { 1508 try { 1509 mSensorManager = (SensorManager) 1510 mAudioService.mContext.getSystemService(Context.SENSOR_SERVICE); 1511 mDynSensorCallback = new HelperDynamicSensorCallback(); 1512 mSensorManager.registerDynamicSensorCallback(mDynSensorCallback); 1513 } catch (Exception e) { 1514 Log.e(TAG, "Error with SensorManager, can't initialize sensors", e); 1515 mSensorManager = null; 1516 mDynSensorCallback = null; 1517 return; 1518 } 1519 } 1520 // initialize sensor handles 1521 // TODO check risk of race condition for updating the association of a head tracker 1522 // and an audio device: 1523 // does this happen before routing is updated? 1524 // avoid by supporting adding device here AND in onRoutingUpdated() 1525 headHandle = getHeadSensorHandleUpdateTracker(); 1526 loglogi("head tracker sensor handle initialized to " + headHandle); 1527 screenHandle = getScreenSensorHandle(); 1528 Log.i(TAG, "found screen sensor handle initialized to " + screenHandle); 1529 } else { 1530 if (mSensorManager != null && mDynSensorCallback != null) { 1531 mSensorManager.unregisterDynamicSensorCallback(mDynSensorCallback); 1532 mSensorManager = null; 1533 mDynSensorCallback = null; 1534 } 1535 // -1 is disable value for both screen and head tracker handles 1536 } 1537 try { 1538 Log.i(TAG, "setScreenSensor:" + screenHandle); 1539 mSpat.setScreenSensor(screenHandle); 1540 } catch (Exception e) { 1541 Log.e(TAG, "Error calling setScreenSensor:" + screenHandle, e); 1542 } 1543 try { 1544 Log.i(TAG, "setHeadSensor:" + headHandle); 1545 mSpat.setHeadSensor(headHandle); 1546 if (mHeadTrackerAvailable != (headHandle != -1)) { 1547 mHeadTrackerAvailable = (headHandle != -1); 1548 dispatchHeadTrackerAvailable(mHeadTrackerAvailable); 1549 } 1550 } catch (Exception e) { 1551 Log.e(TAG, "Error calling setHeadSensor:" + headHandle, e); 1552 } 1553 setDesiredHeadTrackingMode(mDesiredHeadTrackingMode); 1554 } 1555 1556 //------------------------------------------------------ 1557 // SDK <-> AIDL converters headTrackingModeTypeToSpatializerInt(byte mode)1558 private static int headTrackingModeTypeToSpatializerInt(byte mode) { 1559 switch (mode) { 1560 case HeadTracking.Mode.OTHER: 1561 return Spatializer.HEAD_TRACKING_MODE_OTHER; 1562 case HeadTracking.Mode.DISABLED: 1563 return Spatializer.HEAD_TRACKING_MODE_DISABLED; 1564 case HeadTracking.Mode.RELATIVE_WORLD: 1565 return Spatializer.HEAD_TRACKING_MODE_RELATIVE_WORLD; 1566 case HeadTracking.Mode.RELATIVE_SCREEN: 1567 return Spatializer.HEAD_TRACKING_MODE_RELATIVE_DEVICE; 1568 default: 1569 throw (new IllegalArgumentException("Unexpected head tracking mode:" + mode)); 1570 } 1571 } 1572 spatializerIntToHeadTrackingModeType(int sdkMode)1573 private static byte spatializerIntToHeadTrackingModeType(int sdkMode) { 1574 switch (sdkMode) { 1575 case Spatializer.HEAD_TRACKING_MODE_OTHER: 1576 return HeadTracking.Mode.OTHER; 1577 case Spatializer.HEAD_TRACKING_MODE_DISABLED: 1578 return HeadTracking.Mode.DISABLED; 1579 case Spatializer.HEAD_TRACKING_MODE_RELATIVE_WORLD: 1580 return HeadTracking.Mode.RELATIVE_WORLD; 1581 case Spatializer.HEAD_TRACKING_MODE_RELATIVE_DEVICE: 1582 return HeadTracking.Mode.RELATIVE_SCREEN; 1583 default: 1584 throw (new IllegalArgumentException("Unexpected head tracking mode:" + sdkMode)); 1585 } 1586 } 1587 spatializationLevelToSpatializerInt(byte level)1588 private static int spatializationLevelToSpatializerInt(byte level) { 1589 switch (level) { 1590 case Spatialization.Level.NONE: 1591 return Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE; 1592 case Spatialization.Level.MULTICHANNEL: 1593 return Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_MULTICHANNEL; 1594 case Spatialization.Level.BED_PLUS_OBJECTS: 1595 return Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_MCHAN_BED_PLUS_OBJECTS; 1596 default: 1597 throw (new IllegalArgumentException("Unexpected spatializer level:" + level)); 1598 } 1599 } 1600 dump(PrintWriter pw)1601 void dump(PrintWriter pw) { 1602 pw.println("SpatializerHelper:"); 1603 pw.println("\tmState:" + mState); 1604 pw.println("\tmSpatLevel:" + mSpatLevel); 1605 pw.println("\tmCapableSpatLevel:" + mCapableSpatLevel); 1606 pw.println("\tmIsHeadTrackingSupported:" + mIsHeadTrackingSupported); 1607 StringBuilder modesString = new StringBuilder(); 1608 for (int mode : mSupportedHeadTrackingModes) { 1609 modesString.append(Spatializer.headtrackingModeToString(mode)).append(" "); 1610 } 1611 pw.println("\tsupported head tracking modes:" + modesString); 1612 pw.println("\tmDesiredHeadTrackingMode:" 1613 + Spatializer.headtrackingModeToString(mDesiredHeadTrackingMode)); 1614 pw.println("\tmActualHeadTrackingMode:" 1615 + Spatializer.headtrackingModeToString(mActualHeadTrackingMode)); 1616 pw.println("\theadtracker available:" + mHeadTrackerAvailable); 1617 pw.println("\tsupports binaural:" + mBinauralSupported + " / transaural:" 1618 + mTransauralSupported); 1619 pw.println("\tmSpatOutput:" + mSpatOutput); 1620 pw.println("\thas FEATURE_AUDIO_SPATIAL_HEADTRACKING_LOW_LATENCY:" 1621 + mAudioService.mContext.getPackageManager().hasSystemFeature( 1622 PackageManager.FEATURE_AUDIO_SPATIAL_HEADTRACKING_LOW_LATENCY)); 1623 } 1624 spatStateString(int state)1625 private static String spatStateString(int state) { 1626 switch (state) { 1627 case STATE_UNINITIALIZED: 1628 return "STATE_UNINITIALIZED"; 1629 case STATE_NOT_SUPPORTED: 1630 return "STATE_NOT_SUPPORTED"; 1631 case STATE_DISABLED_UNAVAILABLE: 1632 return "STATE_DISABLED_UNAVAILABLE"; 1633 case STATE_ENABLED_UNAVAILABLE: 1634 return "STATE_ENABLED_UNAVAILABLE"; 1635 case STATE_ENABLED_AVAILABLE: 1636 return "STATE_ENABLED_AVAILABLE"; 1637 case STATE_DISABLED_AVAILABLE: 1638 return "STATE_DISABLED_AVAILABLE"; 1639 default: 1640 return "invalid state"; 1641 } 1642 } 1643 getHeadSensorHandleUpdateTracker()1644 private int getHeadSensorHandleUpdateTracker() { 1645 Sensor htSensor = null; 1646 if (sRoutingDevices.isEmpty()) { 1647 logloge("getHeadSensorHandleUpdateTracker: no device, no head tracker"); 1648 return -1; 1649 } 1650 final AudioDeviceAttributes currentDevice = sRoutingDevices.get(0); 1651 List<String> deviceAddresses = mAudioService.getDeviceIdentityAddresses(currentDevice); 1652 // We limit only to Sensor.TYPE_HEAD_TRACKER here to avoid confusion 1653 // with gaming sensors. (Note that Sensor.TYPE_ROTATION_VECTOR 1654 // and Sensor.TYPE_GAME_ROTATION_VECTOR are supported internally by 1655 // SensorPoseProvider). 1656 // Note: this is a dynamic sensor list right now. 1657 List<Sensor> sensors = mSensorManager.getDynamicSensorList(Sensor.TYPE_HEAD_TRACKER); 1658 for (String address : deviceAddresses) { 1659 UUID routingDeviceUuid = UuidUtils.uuidFromAudioDeviceAttributes( 1660 new AudioDeviceAttributes(currentDevice.getInternalType(), address)); 1661 if (dsaOverBtLeAudio()) { 1662 for (Sensor sensor : sensors) { 1663 final UUID uuid = sensor.getUuid(); 1664 if (uuid.equals(routingDeviceUuid)) { 1665 htSensor = sensor; 1666 HeadtrackerInfo info = new HeadtrackerInfo(sensor); 1667 if (isBluetoothLeDevice(currentDevice.getInternalType())) { 1668 if (info.getMajorVersion() == 2) { 1669 // Version 2 is used only by LE Audio profile 1670 break; 1671 } 1672 // we do not break, as this could be a match on the A2DP sensor 1673 // for a dual mode headset. 1674 } else if (info.getMajorVersion() == 1) { 1675 // Version 1 is used only by A2DP profile 1676 break; 1677 } 1678 } 1679 if (htSensor == null && uuid.equals(UuidUtils.STANDALONE_UUID)) { 1680 htSensor = sensor; 1681 // we do not break, perhaps we find a head tracker on device. 1682 } 1683 } 1684 if (htSensor != null) { 1685 if (htSensor.getUuid().equals(UuidUtils.STANDALONE_UUID)) { 1686 break; 1687 } 1688 if (setHasHeadTracker(currentDevice)) { 1689 break; 1690 } else { 1691 htSensor = null; 1692 } 1693 } 1694 } else { 1695 for (Sensor sensor : sensors) { 1696 final UUID uuid = sensor.getUuid(); 1697 if (uuid.equals(routingDeviceUuid)) { 1698 htSensor = sensor; 1699 if (!setHasHeadTracker(currentDevice)) { 1700 htSensor = null; 1701 } 1702 break; 1703 } 1704 if (uuid.equals(UuidUtils.STANDALONE_UUID)) { 1705 htSensor = sensor; 1706 // we do not break, perhaps we find a head tracker on device. 1707 } 1708 } 1709 if (htSensor != null) { 1710 break; 1711 } 1712 } 1713 } 1714 return htSensor != null ? htSensor.getHandle() : -1; 1715 } 1716 1717 /** 1718 * Contains the information parsed from the head tracker sensor version. 1719 * See platform/hardware/libhardware/modules/sensors/dynamic_sensor/HidRawSensor.h 1720 * for the definition of version and capability fields. 1721 */ 1722 private static class HeadtrackerInfo { 1723 private final int mVersion; HeadtrackerInfo(Sensor sensor)1724 HeadtrackerInfo(Sensor sensor) { 1725 mVersion = sensor.getVersion(); 1726 } getMajorVersion()1727 int getMajorVersion() { 1728 return (mVersion & 0xFF000000) >> 24; 1729 } getMinorVersion()1730 int getMinorVersion() { 1731 return (mVersion & 0xFF0000) >> 16; 1732 } hasAclTransport()1733 boolean hasAclTransport() { 1734 return getMajorVersion() == 2 ? ((mVersion & 0x1) != 0) : false; 1735 } hasIsoTransport()1736 boolean hasIsoTransport() { 1737 return getMajorVersion() == 2 ? ((mVersion & 0x2) != 0) : false; 1738 } 1739 }; 1740 getScreenSensorHandle()1741 private int getScreenSensorHandle() { 1742 int screenHandle = -1; 1743 Sensor screenSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR); 1744 if (screenSensor != null) { 1745 screenHandle = screenSensor.getHandle(); 1746 } 1747 return screenHandle; 1748 } 1749 1750 /** 1751 * Returns routing for the given attributes 1752 * @param aa AudioAttributes whose routing is being queried 1753 * @return a non-null never-empty list of devices. If the routing query failed, the list 1754 * will contain null. 1755 */ getRoutingDevices(AudioAttributes aa)1756 private @NonNull ArrayList<AudioDeviceAttributes> getRoutingDevices(AudioAttributes aa) { 1757 final ArrayList<AudioDeviceAttributes> devices = mASA.getDevicesForAttributes( 1758 aa, false /* forVolume */); 1759 for (AudioDeviceAttributes ada : devices) { 1760 if (ada == null) { 1761 // invalid entry, reject this routing query by returning an empty list 1762 return new ArrayList<>(0); 1763 } 1764 } 1765 return devices; 1766 } 1767 loglogi(String msg)1768 private static void loglogi(String msg) { 1769 AudioService.sSpatialLogger.enqueueAndLog(msg, EventLogger.Event.ALOGI, TAG); 1770 } 1771 logloge(String msg)1772 private static String logloge(String msg) { 1773 AudioService.sSpatialLogger.enqueueAndLog(msg, EventLogger.Event.ALOGE, TAG); 1774 return msg; 1775 } 1776 1777 //------------------------------------------------ 1778 // for testing purposes only forceStateForTest(int state)1779 /*package*/ synchronized void forceStateForTest(int state) { 1780 mState = state; 1781 } 1782 initForTest(boolean hasBinaural, boolean hasTransaural)1783 /*package*/ synchronized void initForTest(boolean hasBinaural, boolean hasTransaural) { 1784 mBinauralSupported = hasBinaural; 1785 mTransauralSupported = hasTransaural; 1786 } 1787 } 1788