From c78b49af77407a91b082d60642f3f5f05689d0f4 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Tue, 24 Nov 2020 10:16:45 +0100 Subject: [PATCH] Audio: use preferred device for strategy for communication route Replaced use of forced usage for communication by set preferred device for strategy to control communication route in audio policy manager. Also do not use forced usage for record which was redundant with forced usage for communication. Bug: 161358428 Test: audio smoke tests Change-Id: I665257fc5903b663096104b0424781d9799635a5 --- core/jni/android_media_AudioSystem.cpp | 28 ++-- .../android/media/AudioDeviceAttributes.java | 14 +- media/java/android/media/AudioSystem.java | 2 +- .../server/audio/AudioDeviceBroker.java | 132 +++++++++++++----- .../server/audio/AudioDeviceInventory.java | 4 + .../android/server/audio/AudioService.java | 6 +- .../com/android/server/audio/BtHelper.java | 53 ++++--- 7 files changed, 164 insertions(+), 75 deletions(-) diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp index a95ee4f12c51e..5cffbefbd9a3c 100644 --- a/core/jni/android_media_AudioSystem.cpp +++ b/core/jni/android_media_AudioSystem.cpp @@ -298,20 +298,25 @@ static sp setJniCallback(JNIEnv* env, return old; } -#define check_AudioSystem_Command(status) _check_AudioSystem_Command(__func__, (status)) +#define check_AudioSystem_Command(...) _check_AudioSystem_Command(__func__, __VA_ARGS__) -static int _check_AudioSystem_Command(const char* caller, status_t status) -{ - ALOGE_IF(status, "Command failed for %s: %d", caller, status); +static int _check_AudioSystem_Command(const char *caller, status_t status, + std::vector ignoredErrors = {}) { + int jniStatus = kAudioStatusOk; switch (status) { case DEAD_OBJECT: - return kAudioStatusMediaServerDied; + jniStatus = kAudioStatusMediaServerDied; + break; case NO_ERROR: - return kAudioStatusOk; + break; default: + if (std::find(begin(ignoredErrors), end(ignoredErrors), status) == end(ignoredErrors)) { + jniStatus = kAudioStatusError; + } break; } - return kAudioStatusError; + ALOGE_IF(jniStatus != kAudioStatusOk, "Command failed for %s: %d", caller, status); + return jniStatus; } static jint getVectorOfAudioDeviceTypeAddr(JNIEnv *env, jintArray deviceTypes, @@ -2350,9 +2355,12 @@ static jint android_media_AudioSystem_setDevicesRoleForStrategy(JNIEnv *env, job static jint android_media_AudioSystem_removeDevicesRoleForStrategy(JNIEnv *env, jobject thiz, jint strategy, jint role) { - return (jint)check_AudioSystem_Command( - AudioSystem::removeDevicesRoleForStrategy((product_strategy_t)strategy, - (device_role_t)role)); + return (jint) + check_AudioSystem_Command(AudioSystem::removeDevicesRoleForStrategy((product_strategy_t) + strategy, + (device_role_t) + role), + {NAME_NOT_FOUND}); } static jint android_media_AudioSystem_getDevicesForRoleAndStrategy(JNIEnv *env, jobject thiz, diff --git a/media/java/android/media/AudioDeviceAttributes.java b/media/java/android/media/AudioDeviceAttributes.java index 6c8b50037d3d0..7caac899a6034 100644 --- a/media/java/android/media/AudioDeviceAttributes.java +++ b/media/java/android/media/AudioDeviceAttributes.java @@ -120,7 +120,13 @@ public final class AudioDeviceAttributes implements Parcelable { mAddress = address; } - /*package*/ AudioDeviceAttributes(int nativeType, @NonNull String address) { + /** + * @hide + * Constructor from internal device type and address + * @param type the internal device type, as defined in {@link AudioSystem} + * @param address the address of the device, or an empty string for devices without one + */ + public AudioDeviceAttributes(int nativeType, @NonNull String address) { mRole = (nativeType & AudioSystem.DEVICE_BIT_IN) != 0 ? ROLE_INPUT : ROLE_OUTPUT; mType = AudioDeviceInfo.convertInternalDeviceToDeviceType(nativeType); mAddress = address; @@ -191,10 +197,8 @@ public final class AudioDeviceAttributes implements Parcelable { public String toString() { return new String("AudioDeviceAttributes:" + " role:" + roleToString(mRole) - + " type:" + (mRole == ROLE_OUTPUT ? AudioSystem.getOutputDeviceName( - AudioDeviceInfo.convertDeviceTypeToInternalDevice(mType)) - : AudioSystem.getInputDeviceName( - AudioDeviceInfo.convertDeviceTypeToInternalDevice(mType))) + + " type:" + (mRole == ROLE_OUTPUT ? AudioSystem.getOutputDeviceName(mNativeType) + : AudioSystem.getInputDeviceName(mNativeType)) + " addr:" + mAddress); } diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java index 8987a713e505a..164b194996a6c 100644 --- a/media/java/android/media/AudioSystem.java +++ b/media/java/android/media/AudioSystem.java @@ -1730,7 +1730,7 @@ public class AudioSystem int[] types = new int[devices.size()]; String[] addresses = new String[devices.size()]; for (int i = 0; i < devices.size(); ++i) { - types[i] = AudioDeviceInfo.convertDeviceTypeToInternalDevice(devices.get(i).getType()); + types[i] = devices.get(i).getInternalType(); addresses[i] = devices.get(i).getAddress(); } return setDevicesRoleForStrategy(strategy, role, types, addresses); diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java index f04c5eb030dba..26f5c4ca1e802 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java +++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java @@ -16,6 +16,7 @@ package com.android.server.audio; import android.annotation.NonNull; +import android.annotation.Nullable; import android.bluetooth.BluetoothA2dp; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHeadset; @@ -34,6 +35,7 @@ import android.media.ICapturePresetDevicesRoleDispatcher; import android.media.ICommunicationDeviceDispatcher; import android.media.IStrategyPreferredDevicesDispatcher; import android.media.MediaMetrics; +import android.media.audiopolicy.AudioProductStrategy; import android.os.Binder; import android.os.Handler; import android.os.IBinder; @@ -51,6 +53,7 @@ import android.util.PrintWriterPrinter; import com.android.internal.annotations.GuardedBy; import java.io.PrintWriter; +import java.util.Arrays; import java.util.HashSet; import java.util.LinkedList; import java.util.List; @@ -77,7 +80,8 @@ import java.util.concurrent.atomic.AtomicBoolean; private final @NonNull Context mContext; /** Forced device usage for communications sent to AudioSystem */ - private int mForcedUseForComm; + private AudioDeviceAttributes mPreferredDeviceforComm; + private int mCommunicationStrategyId = -1; // Manages all connected devices, only ever accessed on the message loop private final AudioDeviceInventory mDeviceInventory; @@ -133,10 +137,23 @@ import java.util.concurrent.atomic.AtomicBoolean; init(); } + private void initCommunicationStrategyId() { + List strategies = AudioProductStrategy.getAudioProductStrategies(); + for (AudioProductStrategy strategy : strategies) { + if (strategy.getAudioAttributesForLegacyStreamType(AudioSystem.STREAM_VOICE_CALL) + != null) { + mCommunicationStrategyId = strategy.getId(); + return; + } + } + mCommunicationStrategyId = -1; + } + private void init() { setupMessaging(mContext); - mForcedUseForComm = AudioSystem.FORCE_NONE; + mPreferredDeviceforComm = null; + initCommunicationStrategyId(); } /*package*/ Context getContext() { @@ -221,8 +238,7 @@ import java.util.concurrent.atomic.AtomicBoolean; synchronized (mDeviceStateLock) { AudioDeviceAttributes device = null; if (on) { - device = new AudioDeviceAttributes(AudioDeviceAttributes.ROLE_OUTPUT, - AudioDeviceInfo.TYPE_BUILTIN_SPEAKER, ""); + device = new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_SPEAKER, ""); } else { CommunicationRouteClient client = getCommunicationRouteClientForPid(pid); if (client == null || !client.requestsSpeakerphone()) { @@ -275,6 +291,10 @@ import java.util.concurrent.atomic.AtomicBoolean; if (AudioService.DEBUG_COMM_RTE) { Log.v(TAG, "setCommunicationRouteForClient: device: " + device); } + AudioService.sDeviceLogger.log((new AudioEventLogger.StringEvent( + "setCommunicationRouteForClient for pid: " + pid + + " device: " + device + + " from API: " + eventSource)).printLog(TAG)); final boolean wasBtScoRequested = isBluetoothScoRequested(); final boolean wasSpeakerphoneRequested = isSpeakerphoneRequested(); @@ -393,7 +413,11 @@ import java.util.concurrent.atomic.AtomicBoolean; * @return true if speakerphone is active, false otherwise. */ /*package*/ boolean isSpeakerphoneOn() { - return getForcedUseForComm() == AudioSystem.FORCE_SPEAKER; + AudioDeviceAttributes device = getPreferredDeviceForComm(); + if (device == null) { + return false; + } + return device.getInternalType() == AudioSystem.DEVICE_OUT_SPEAKER; } /** @@ -560,7 +584,11 @@ import java.util.concurrent.atomic.AtomicBoolean; * @return true if Bluetooth SCO is active , false otherwise. */ /*package*/ boolean isBluetoothScoOn() { - return getForcedUseForComm() == AudioSystem.FORCE_BT_SCO; + AudioDeviceAttributes device = getPreferredDeviceForComm(); + if (device == null) { + return false; + } + return AudioSystem.DEVICE_OUT_ALL_SCO_SET.contains(device.getInternalType()); } /*package*/ AudioRoutesInfo startWatchingRoutes(IAudioRoutesObserver observer) { @@ -612,8 +640,8 @@ import java.util.concurrent.atomic.AtomicBoolean; synchronized (mSetModeLock) { synchronized (mDeviceStateLock) { - AudioDeviceAttributes device = new AudioDeviceAttributes( - AudioDeviceAttributes.ROLE_OUTPUT, AudioDeviceInfo.TYPE_BLUETOOTH_SCO, ""); + AudioDeviceAttributes device = + new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_BLUETOOTH_SCO, ""); setCommunicationRouteForClient(cb, pid, device, scoAudioMode, eventSource); } } @@ -643,10 +671,19 @@ import java.util.concurrent.atomic.AtomicBoolean; return mDeviceInventory.setPreferredDevicesForStrategySync(strategy, devices); } + /*package*/ void postSetPreferredDevicesForStrategy(int strategy, + @NonNull List devices) { + sendILMsgNoDelay(MSG_IL_SET_PREF_DEVICES_FOR_STRATEGY, SENDMSG_REPLACE, strategy, devices); + } + /*package*/ int removePreferredDevicesForStrategySync(int strategy) { return mDeviceInventory.removePreferredDevicesForStrategySync(strategy); } + /*package*/ void postRemovePreferredDevicesForStrategy(int strategy) { + sendIMsgNoDelay(MSG_I_REMOVE_PREF_DEVICES_FOR_STRATEGY, SENDMSG_REPLACE, strategy); + } + /*package*/ void registerStrategyPreferredDevicesDispatcher( @NonNull IStrategyPreferredDevicesDispatcher dispatcher) { mDeviceInventory.registerStrategyPreferredDevicesDispatcher(dispatcher); @@ -971,9 +1008,12 @@ import java.util.concurrent.atomic.AtomicBoolean; pw.println(" " + prefix + "pid: " + cl.getPid() + " device: " + cl.getDevice() + " cb: " + cl.getBinder()); }); - pw.println("\n" + prefix + "mForcedUseForComm: " - + AudioSystem.forceUseConfigToString(mForcedUseForComm)); - pw.println(prefix + "mModeOwnerPid: " + mModeOwnerPid); + pw.println("\n" + prefix + "mPreferredDeviceforComm: " + + mPreferredDeviceforComm); + pw.println(prefix + "mCommunicationStrategyId: " + + mCommunicationStrategyId); + + pw.println("\n" + prefix + "mModeOwnerPid: " + mModeOwnerPid); mBtHelper.dump(pw, prefix); } @@ -1068,6 +1108,7 @@ import java.util.concurrent.atomic.AtomicBoolean; case MSG_RESTORE_DEVICES: synchronized (mSetModeLock) { synchronized (mDeviceStateLock) { + initCommunicationStrategyId(); mDeviceInventory.onRestoreDevices(); mBtHelper.onAudioServerDiedRestoreA2dp(); onUpdateCommunicationRoute("MSG_RESTORE_DEVICES"); @@ -1277,6 +1318,17 @@ import java.util.concurrent.atomic.AtomicBoolean; final int strategy = msg.arg1; mDeviceInventory.onSaveRemovePreferredDevices(strategy); } break; + case MSG_IL_SET_PREF_DEVICES_FOR_STRATEGY: { + final int strategy = msg.arg1; + final List devices = + (List) msg.obj; + setPreferredDevicesForStrategySync(strategy, devices); + + } break; + case MSG_I_REMOVE_PREF_DEVICES_FOR_STRATEGY: { + final int strategy = msg.arg1; + removePreferredDevicesForStrategySync(strategy); + } break; case MSG_CHECK_MUTE_MUSIC: checkMessagesMuteMusic(0); break; @@ -1369,7 +1421,8 @@ import java.util.concurrent.atomic.AtomicBoolean; private static final int MSG_I_SAVE_CLEAR_PREF_DEVICES_FOR_CAPTURE_PRESET = 38; private static final int MSG_L_UPDATE_COMMUNICATION_ROUTE = 39; - + private static final int MSG_IL_SET_PREF_DEVICES_FOR_STRATEGY = 40; + private static final int MSG_I_REMOVE_PREF_DEVICES_FOR_STRATEGY = 41; private static boolean isMessageHandledUnderWakelock(int msgId) { switch(msgId) { @@ -1609,16 +1662,25 @@ import java.util.concurrent.atomic.AtomicBoolean; * @return selected forced usage for communication. */ @GuardedBy("mDeviceStateLock") - private int getForcedUseForComm() { + @Nullable private AudioDeviceAttributes getPreferredDeviceForComm() { boolean btSCoOn = mBluetoothScoOn && mBtHelper.isBluetoothScoOn(); - if (btSCoOn) { - return AudioSystem.FORCE_BT_SCO; + // Use the SCO device known to BtHelper so that it matches exactly + // what has been communicated to audio policy manager. The device + // returned by requestedCommunicationDevice() can be a dummy SCO device if legacy + // APIs are used to start SCO audio. + AudioDeviceAttributes device = mBtHelper.getHeadsetAudioDevice(); + if (device != null) { + return device; + } } - if (isSpeakerphoneRequested()) { - return AudioSystem.FORCE_SPEAKER; + AudioDeviceAttributes device = requestedCommunicationDevice(); + if (device == null + || AudioSystem.DEVICE_OUT_ALL_SCO_SET.contains(device.getInternalType())) { + // Do not indicate BT SCO selection if SCO is requested but SCO is not ON + return null; } - return AudioSystem.FORCE_NONE; + return device; } /** @@ -1628,30 +1690,24 @@ import java.util.concurrent.atomic.AtomicBoolean; // @GuardedBy("mSetModeLock") @GuardedBy("mDeviceStateLock") private void onUpdateCommunicationRoute(String eventSource) { - mForcedUseForComm = getForcedUseForComm(); - + mPreferredDeviceforComm = getPreferredDeviceForComm(); if (AudioService.DEBUG_COMM_RTE) { - Log.v(TAG, "onUpdateCommunicationRoute, mForcedUseForComm: " + mForcedUseForComm - + " eventSource: " + eventSource); + Log.v(TAG, "onUpdateCommunicationRoute, mPreferredDeviceforComm: " + + mPreferredDeviceforComm + " eventSource: " + eventSource); } - if (mForcedUseForComm == AudioSystem.FORCE_BT_SCO) { - AudioSystem.setParameters("BT_SCO=on"); - setForceUse_Async( - AudioSystem.FOR_COMMUNICATION, AudioSystem.FORCE_BT_SCO, eventSource); - setForceUse_Async( - AudioSystem.FOR_RECORD, AudioSystem.FORCE_BT_SCO, eventSource); - } else { + if (mPreferredDeviceforComm == null + || !AudioSystem.DEVICE_OUT_ALL_SCO_SET.contains( + mPreferredDeviceforComm.getInternalType())) { AudioSystem.setParameters("BT_SCO=off"); - setForceUse_Async( - AudioSystem.FOR_RECORD, AudioSystem.FORCE_NONE, eventSource); - if (mForcedUseForComm == AudioSystem.FORCE_SPEAKER) { - setForceUse_Async( - AudioSystem.FOR_COMMUNICATION, AudioSystem.FORCE_SPEAKER, eventSource); - } else { - setForceUse_Async( - AudioSystem.FOR_COMMUNICATION, AudioSystem.FORCE_NONE, eventSource); - } + } else { + AudioSystem.setParameters("BT_SCO=on"); + } + if (mPreferredDeviceforComm == null) { + postRemovePreferredDevicesForStrategy(mCommunicationStrategyId); + } else { + postSetPreferredDevicesForStrategy( + mCommunicationStrategyId, Arrays.asList(mPreferredDeviceforComm)); } mAudioService.postUpdateRingerModeServiceInt(); dispatchCommunicationDevice(); diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java index 33a8a30243de0..82586b8f9b235 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java +++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java @@ -648,6 +648,10 @@ public class AudioDeviceInventory { /*package*/ int setPreferredDevicesForStrategySync(int strategy, @NonNull List devices) { final long identity = Binder.clearCallingIdentity(); + + AudioService.sDeviceLogger.log((new AudioEventLogger.StringEvent( + "setPreferredDevicesForStrategySync, strategy: " + strategy + + " devices: " + devices)).printLog(TAG)); final int status = mAudioSystem.setDevicesRoleForStrategy( strategy, AudioSystem.DEVICE_ROLE_PREFERRED, devices); Binder.restoreCallingIdentity(identity); diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 1db8ceb286415..4444de0c00c49 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -5121,8 +5121,7 @@ public class AudioService extends IAudioService.Stub switch (mPlatformType) { case AudioSystem.PLATFORM_VOICE: if (isInCommunication()) { - if (mAudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION) - == AudioSystem.FORCE_BT_SCO) { + if (mDeviceBroker.isBluetoothScoOn()) { // Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO..."); return AudioSystem.STREAM_BLUETOOTH_SCO; } else { @@ -5158,8 +5157,7 @@ public class AudioService extends IAudioService.Stub } default: if (isInCommunication()) { - if (mAudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION) - == AudioSystem.FORCE_BT_SCO) { + if (mDeviceBroker.isBluetoothScoOn()) { if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO"); return AudioSystem.STREAM_BLUETOOTH_SCO; } else { diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java index ae0e805a360a5..c57d5afa5f0ca 100644 --- a/services/core/java/com/android/server/audio/BtHelper.java +++ b/services/core/java/com/android/server/audio/BtHelper.java @@ -27,6 +27,7 @@ import android.bluetooth.BluetoothHeadset; import android.bluetooth.BluetoothHearingAid; import android.bluetooth.BluetoothProfile; import android.content.Intent; +import android.media.AudioDeviceAttributes; import android.media.AudioManager; import android.media.AudioSystem; import android.os.Binder; @@ -530,46 +531,64 @@ public class BtHelper { mDeviceBroker.postBroadcastScoConnectionState(state); } - private boolean handleBtScoActiveDeviceChange(BluetoothDevice btDevice, boolean isActive) { - if (btDevice == null) { - return true; + @Nullable AudioDeviceAttributes getHeadsetAudioDevice() { + if (mBluetoothHeadsetDevice == null) { + return null; } + return btHeadsetDeviceToAudioDevice(mBluetoothHeadsetDevice); + } + + private AudioDeviceAttributes btHeadsetDeviceToAudioDevice(BluetoothDevice btDevice) { String address = btDevice.getAddress(); + if (!BluetoothAdapter.checkBluetoothAddress(address)) { + address = ""; + } BluetoothClass btClass = btDevice.getBluetoothClass(); - int inDevice = AudioSystem.DEVICE_IN_BLUETOOTH_SCO_HEADSET; - int[] outDeviceTypes = { - AudioSystem.DEVICE_OUT_BLUETOOTH_SCO, - AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET, - AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT - }; + int nativeType = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO; if (btClass != null) { switch (btClass.getDeviceClass()) { case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET: case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE: - outDeviceTypes = new int[] { AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET }; + nativeType = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET; break; case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO: - outDeviceTypes = new int[] { AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT }; + nativeType = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT; break; } } - if (!BluetoothAdapter.checkBluetoothAddress(address)) { - address = ""; + if (AudioService.DEBUG_DEVICES) { + Log.i(TAG, "btHeadsetDeviceToAudioDevice btDevice: " + btDevice + + " btClass: " + (btClass == null ? "Unknown" : btClass) + + " nativeType: " + nativeType + " address: " + address); } + return new AudioDeviceAttributes(nativeType, address); + } + + private boolean handleBtScoActiveDeviceChange(BluetoothDevice btDevice, boolean isActive) { + if (btDevice == null) { + return true; + } + int inDevice = AudioSystem.DEVICE_IN_BLUETOOTH_SCO_HEADSET; + AudioDeviceAttributes audioDevice = btHeadsetDeviceToAudioDevice(btDevice); String btDeviceName = getName(btDevice); boolean result = false; if (isActive) { - result |= mDeviceBroker.handleDeviceConnection( - isActive, outDeviceTypes[0], address, btDeviceName); + result |= mDeviceBroker.handleDeviceConnection(isActive, audioDevice.getInternalType(), + audioDevice.getAddress(), btDeviceName); } else { + int[] outDeviceTypes = { + AudioSystem.DEVICE_OUT_BLUETOOTH_SCO, + AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET, + AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT + }; for (int outDeviceType : outDeviceTypes) { result |= mDeviceBroker.handleDeviceConnection( - isActive, outDeviceType, address, btDeviceName); + isActive, outDeviceType, audioDevice.getAddress(), btDeviceName); } } // handleDeviceConnection() && result to make sure the method get executed result = mDeviceBroker.handleDeviceConnection( - isActive, inDevice, address, btDeviceName) && result; + isActive, inDevice, audioDevice.getAddress(), btDeviceName) && result; return result; }