Merge changes from topic "comm_routing"
* changes: Audio: use preferred device for strategy for communication route AudioManager: Add communication device management APIs AudioService: refactor communication route control
This commit is contained in:
@@ -19373,14 +19373,17 @@ package android.media {
|
|||||||
public class AudioManager {
|
public class AudioManager {
|
||||||
method @Deprecated public int abandonAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener);
|
method @Deprecated public int abandonAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener);
|
||||||
method public int abandonAudioFocusRequest(@NonNull android.media.AudioFocusRequest);
|
method public int abandonAudioFocusRequest(@NonNull android.media.AudioFocusRequest);
|
||||||
|
method public void addOnCommunicationDeviceChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.OnCommunicationDeviceChangedListener);
|
||||||
method public void adjustStreamVolume(int, int, int);
|
method public void adjustStreamVolume(int, int, int);
|
||||||
method public void adjustSuggestedStreamVolume(int, int, int);
|
method public void adjustSuggestedStreamVolume(int, int, int);
|
||||||
method public void adjustVolume(int, int);
|
method public void adjustVolume(int, int);
|
||||||
|
method public void clearDeviceForCommunication();
|
||||||
method public void dispatchMediaKeyEvent(android.view.KeyEvent);
|
method public void dispatchMediaKeyEvent(android.view.KeyEvent);
|
||||||
method public int generateAudioSessionId();
|
method public int generateAudioSessionId();
|
||||||
method @NonNull public java.util.List<android.media.AudioPlaybackConfiguration> getActivePlaybackConfigurations();
|
method @NonNull public java.util.List<android.media.AudioPlaybackConfiguration> getActivePlaybackConfigurations();
|
||||||
method @NonNull public java.util.List<android.media.AudioRecordingConfiguration> getActiveRecordingConfigurations();
|
method @NonNull public java.util.List<android.media.AudioRecordingConfiguration> getActiveRecordingConfigurations();
|
||||||
method public int getAllowedCapturePolicy();
|
method public int getAllowedCapturePolicy();
|
||||||
|
method @Nullable public android.media.AudioDeviceInfo getDeviceForCommunication();
|
||||||
method public android.media.AudioDeviceInfo[] getDevices(int);
|
method public android.media.AudioDeviceInfo[] getDevices(int);
|
||||||
method public java.util.List<android.media.MicrophoneInfo> getMicrophones() throws java.io.IOException;
|
method public java.util.List<android.media.MicrophoneInfo> getMicrophones() throws java.io.IOException;
|
||||||
method public int getMode();
|
method public int getMode();
|
||||||
@@ -19415,11 +19418,13 @@ package android.media {
|
|||||||
method @Deprecated public void registerMediaButtonEventReceiver(android.app.PendingIntent);
|
method @Deprecated public void registerMediaButtonEventReceiver(android.app.PendingIntent);
|
||||||
method @Deprecated public void registerRemoteControlClient(android.media.RemoteControlClient);
|
method @Deprecated public void registerRemoteControlClient(android.media.RemoteControlClient);
|
||||||
method @Deprecated public boolean registerRemoteController(android.media.RemoteController);
|
method @Deprecated public boolean registerRemoteController(android.media.RemoteController);
|
||||||
|
method public void removeOnCommunicationDeviceChangedListener(@NonNull android.media.AudioManager.OnCommunicationDeviceChangedListener);
|
||||||
method @Deprecated public int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, int, int);
|
method @Deprecated public int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, int, int);
|
||||||
method public int requestAudioFocus(@NonNull android.media.AudioFocusRequest);
|
method public int requestAudioFocus(@NonNull android.media.AudioFocusRequest);
|
||||||
method public void setAllowedCapturePolicy(int);
|
method public void setAllowedCapturePolicy(int);
|
||||||
method @Deprecated public void setBluetoothA2dpOn(boolean);
|
method @Deprecated public void setBluetoothA2dpOn(boolean);
|
||||||
method public void setBluetoothScoOn(boolean);
|
method public void setBluetoothScoOn(boolean);
|
||||||
|
method public boolean setDeviceForCommunication(@NonNull android.media.AudioDeviceInfo);
|
||||||
method public void setMicrophoneMute(boolean);
|
method public void setMicrophoneMute(boolean);
|
||||||
method public void setMode(int);
|
method public void setMode(int);
|
||||||
method public void setParameters(String);
|
method public void setParameters(String);
|
||||||
@@ -19554,6 +19559,10 @@ package android.media {
|
|||||||
method public void onAudioFocusChange(int);
|
method public void onAudioFocusChange(int);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static interface AudioManager.OnCommunicationDeviceChangedListener {
|
||||||
|
method public void onCommunicationDeviceChanged(@Nullable android.media.AudioDeviceInfo);
|
||||||
|
}
|
||||||
|
|
||||||
public final class AudioMetadata {
|
public final class AudioMetadata {
|
||||||
method @NonNull public static android.media.AudioMetadataMap createMap();
|
method @NonNull public static android.media.AudioMetadataMap createMap();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -850,6 +850,7 @@ package android.media {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public class AudioManager {
|
public class AudioManager {
|
||||||
|
method @Nullable public static android.media.AudioDeviceInfo getDeviceInfoFromType(int);
|
||||||
method public boolean hasRegisteredDynamicPolicy();
|
method public boolean hasRegisteredDynamicPolicy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -294,20 +294,25 @@ static sp<JNIAudioPortCallback> setJniCallback(JNIEnv* env,
|
|||||||
return old;
|
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)
|
static int _check_AudioSystem_Command(const char *caller, status_t status,
|
||||||
{
|
std::vector<status_t> ignoredErrors = {}) {
|
||||||
ALOGE_IF(status, "Command failed for %s: %d", caller, status);
|
int jniStatus = kAudioStatusOk;
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case DEAD_OBJECT:
|
case DEAD_OBJECT:
|
||||||
return kAudioStatusMediaServerDied;
|
jniStatus = kAudioStatusMediaServerDied;
|
||||||
|
break;
|
||||||
case NO_ERROR:
|
case NO_ERROR:
|
||||||
return kAudioStatusOk;
|
break;
|
||||||
default:
|
default:
|
||||||
|
if (std::find(begin(ignoredErrors), end(ignoredErrors), status) == end(ignoredErrors)) {
|
||||||
|
jniStatus = kAudioStatusError;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return kAudioStatusError;
|
ALOGE_IF(jniStatus != kAudioStatusOk, "Command failed for %s: %d", caller, status);
|
||||||
|
return jniStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
static jint getVectorOfAudioDeviceTypeAddr(JNIEnv *env, jintArray deviceTypes,
|
static jint getVectorOfAudioDeviceTypeAddr(JNIEnv *env, jintArray deviceTypes,
|
||||||
@@ -2381,9 +2386,12 @@ static jint android_media_AudioSystem_setDevicesRoleForStrategy(JNIEnv *env, job
|
|||||||
|
|
||||||
static jint android_media_AudioSystem_removeDevicesRoleForStrategy(JNIEnv *env, jobject thiz,
|
static jint android_media_AudioSystem_removeDevicesRoleForStrategy(JNIEnv *env, jobject thiz,
|
||||||
jint strategy, jint role) {
|
jint strategy, jint role) {
|
||||||
return (jint)check_AudioSystem_Command(
|
return (jint)
|
||||||
AudioSystem::removeDevicesRoleForStrategy((product_strategy_t)strategy,
|
check_AudioSystem_Command(AudioSystem::removeDevicesRoleForStrategy((product_strategy_t)
|
||||||
(device_role_t)role));
|
strategy,
|
||||||
|
(device_role_t)
|
||||||
|
role),
|
||||||
|
{NAME_NOT_FOUND});
|
||||||
}
|
}
|
||||||
|
|
||||||
static jint android_media_AudioSystem_getDevicesForRoleAndStrategy(JNIEnv *env, jobject thiz,
|
static jint android_media_AudioSystem_getDevicesForRoleAndStrategy(JNIEnv *env, jobject thiz,
|
||||||
|
|||||||
@@ -120,7 +120,13 @@ public final class AudioDeviceAttributes implements Parcelable {
|
|||||||
mAddress = address;
|
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;
|
mRole = (nativeType & AudioSystem.DEVICE_BIT_IN) != 0 ? ROLE_INPUT : ROLE_OUTPUT;
|
||||||
mType = AudioDeviceInfo.convertInternalDeviceToDeviceType(nativeType);
|
mType = AudioDeviceInfo.convertInternalDeviceToDeviceType(nativeType);
|
||||||
mAddress = address;
|
mAddress = address;
|
||||||
@@ -191,10 +197,8 @@ public final class AudioDeviceAttributes implements Parcelable {
|
|||||||
public String toString() {
|
public String toString() {
|
||||||
return new String("AudioDeviceAttributes:"
|
return new String("AudioDeviceAttributes:"
|
||||||
+ " role:" + roleToString(mRole)
|
+ " role:" + roleToString(mRole)
|
||||||
+ " type:" + (mRole == ROLE_OUTPUT ? AudioSystem.getOutputDeviceName(
|
+ " type:" + (mRole == ROLE_OUTPUT ? AudioSystem.getOutputDeviceName(mNativeType)
|
||||||
AudioDeviceInfo.convertDeviceTypeToInternalDevice(mType))
|
: AudioSystem.getInputDeviceName(mNativeType))
|
||||||
: AudioSystem.getInputDeviceName(
|
|
||||||
AudioDeviceInfo.convertDeviceTypeToInternalDevice(mType)))
|
|
||||||
+ " addr:" + mAddress);
|
+ " addr:" + mAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6123,6 +6123,29 @@ public class AudioManager {
|
|||||||
return infoListFromPortList(ports, flags);
|
return infoListFromPortList(ports, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an {@link AudioDeviceInfo} corresponding to the specified {@link AudioPort} ID.
|
||||||
|
* @param portId The audio port ID to look up for.
|
||||||
|
* @param flags A set of bitflags specifying the criteria to test.
|
||||||
|
* @see #GET_DEVICES_OUTPUTS
|
||||||
|
* @see #GET_DEVICES_INPUTS
|
||||||
|
* @see #GET_DEVICES_ALL
|
||||||
|
* @return An AudioDeviceInfo or null if no device with matching port ID is found.
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public static AudioDeviceInfo getDeviceForPortId(int portId, int flags) {
|
||||||
|
if (portId == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
AudioDeviceInfo[] devices = getDevicesStatic(flags);
|
||||||
|
for (AudioDeviceInfo device : devices) {
|
||||||
|
if (device.getId() == portId) {
|
||||||
|
return device;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers an {@link AudioDeviceCallback} object to receive notifications of changes
|
* Registers an {@link AudioDeviceCallback} object to receive notifications of changes
|
||||||
* to the set of connected audio devices.
|
* to the set of connected audio devices.
|
||||||
@@ -6666,6 +6689,297 @@ public class AudioManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Selects the audio device that should be used for communication use cases, for instance voice
|
||||||
|
* or video calls. This method can be used by voice or video chat applications to select a
|
||||||
|
* different audio device than the one selected by default by the platform.
|
||||||
|
* <p>The device selection is expressed as an {@link AudioDeviceInfo}, of role sink
|
||||||
|
* ({@link AudioDeviceInfo#isSink()} is <code>true</code>) and of one of the following types:
|
||||||
|
* <ul>
|
||||||
|
* <li> {@link AudioDeviceInfo#TYPE_BUILTIN_EARPIECE}
|
||||||
|
* <li> {@link AudioDeviceInfo#TYPE_BUILTIN_SPEAKER}
|
||||||
|
* <li> {@link AudioDeviceInfo#TYPE_WIRED_HEADSET}
|
||||||
|
* <li> {@link AudioDeviceInfo#TYPE_BLUETOOTH_SCO}
|
||||||
|
* <li> {@link AudioDeviceInfo#TYPE_USB_HEADSET}
|
||||||
|
* <li> {@link AudioDeviceInfo#TYPE_BLE_HEADSET}
|
||||||
|
* </ul>
|
||||||
|
* The selection is active as long as the requesting application lives, until
|
||||||
|
* {@link #clearDeviceForCommunication} is called or until the device is disconnected.
|
||||||
|
* It is therefore important for applications to clear the request when a call ends or the
|
||||||
|
* application is paused.
|
||||||
|
* <p>In case of simultaneous requests by multiple applications the priority is given to the
|
||||||
|
* application currently controlling the audio mode (see {@link #setMode(int)}). This is the
|
||||||
|
* latest application having selected mode {@link #MODE_IN_COMMUNICATION} or mode
|
||||||
|
* {@link #MODE_IN_CALL}. Note that <code>MODE_IN_CALL</code> can only be selected by the main
|
||||||
|
* telephony application with permission
|
||||||
|
* {@link android.Manifest.permission#MODIFY_PHONE_STATE}.
|
||||||
|
* <p> If the requested devices is not currently available, the request will be rejected and
|
||||||
|
* the method will return false.
|
||||||
|
* <p>This API replaces the following deprecated APIs:
|
||||||
|
* <ul>
|
||||||
|
* <li> {@link #startBluetoothSco()}
|
||||||
|
* <li> {@link #stopBluetoothSco()}
|
||||||
|
* <li> {@link #setSpeakerphoneOn(boolean)}
|
||||||
|
* </ul>
|
||||||
|
* <h4>Example</h4>
|
||||||
|
* <p>The example below shows how to enable and disable speakerphone mode.
|
||||||
|
* <pre class="prettyprint">
|
||||||
|
* // Get an AudioManager instance
|
||||||
|
* AudioManager audioManager = Context.getSystemService(AudioManager.class);
|
||||||
|
* try {
|
||||||
|
* AudioDeviceInfo speakerDevice = null;
|
||||||
|
* AudioDeviceInfo[] devices = audioManager.getDevices(GET_DEVICES_OUTPUTS);
|
||||||
|
* for (AudioDeviceInfo device : devices) {
|
||||||
|
* if (device.getType() == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER) {
|
||||||
|
* speakerDevice = device;
|
||||||
|
* break;
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* if (speakerDevice != null) {
|
||||||
|
* // Turn speakerphone ON.
|
||||||
|
* boolean result = audioManager.setDeviceForCommunication(speakerDevice);
|
||||||
|
* if (!result) {
|
||||||
|
* // Handle error.
|
||||||
|
* }
|
||||||
|
* // Turn speakerphone OFF.
|
||||||
|
* audioManager.clearDeviceForCommunication();
|
||||||
|
* }
|
||||||
|
* } catch (IllegalArgumentException e) {
|
||||||
|
* // Handle exception.
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
* @param device the requested audio device.
|
||||||
|
* @return <code>true</code> if the request was accepted, <code>false</code> otherwise.
|
||||||
|
* @throws IllegalArgumentException If an invalid device is specified.
|
||||||
|
*/
|
||||||
|
public boolean setDeviceForCommunication(@NonNull AudioDeviceInfo device) {
|
||||||
|
Objects.requireNonNull(device);
|
||||||
|
try {
|
||||||
|
if (device.getId() == 0) {
|
||||||
|
throw new IllegalArgumentException("In valid device: " + device);
|
||||||
|
}
|
||||||
|
return getService().setDeviceForCommunication(mICallBack, device.getId());
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
throw e.rethrowFromSystemServer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancels previous communication device selection made with
|
||||||
|
* {@link #setDeviceForCommunication(AudioDeviceInfo)}.
|
||||||
|
*/
|
||||||
|
public void clearDeviceForCommunication() {
|
||||||
|
try {
|
||||||
|
getService().setDeviceForCommunication(mICallBack, 0);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
throw e.rethrowFromSystemServer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns currently selected audio device for communication.
|
||||||
|
* <p>This API replaces the following deprecated APIs:
|
||||||
|
* <ul>
|
||||||
|
* <li> {@link #isBluetoothScoOn()}
|
||||||
|
* <li> {@link #isSpeakerphoneOn()}
|
||||||
|
* </ul>
|
||||||
|
* @return an {@link AudioDeviceInfo} indicating which audio device is
|
||||||
|
* currently selected or communication use cases or null if default selection
|
||||||
|
* is used.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public AudioDeviceInfo getDeviceForCommunication() {
|
||||||
|
try {
|
||||||
|
return getDeviceForPortId(
|
||||||
|
getService().getDeviceForCommunication(), GET_DEVICES_OUTPUTS);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
throw e.rethrowFromSystemServer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @hide
|
||||||
|
* Returns an {@link AudioDeviceInfo} corresponding to a connected device of the type provided.
|
||||||
|
* The type must be a valid output type defined in <code>AudioDeviceInfo</code> class,
|
||||||
|
* for instance {@link AudioDeviceInfo#TYPE_BUILTIN_SPEAKER}.
|
||||||
|
* The method will return null if no device of the provided type is connected.
|
||||||
|
* If more than one device of the provided type is connected, an object corresponding to the
|
||||||
|
* first device encountered in the enumeration list will be returned.
|
||||||
|
* @param deviceType The device device for which an <code>AudioDeviceInfo</code>
|
||||||
|
* object is queried.
|
||||||
|
* @return An AudioDeviceInfo object or null if no device with the requested type is connected.
|
||||||
|
* @throws IllegalArgumentException If an invalid device type is specified.
|
||||||
|
*/
|
||||||
|
@TestApi
|
||||||
|
@Nullable
|
||||||
|
public static AudioDeviceInfo getDeviceInfoFromType(
|
||||||
|
@AudioDeviceInfo.AudioDeviceTypeOut int deviceType) {
|
||||||
|
AudioDeviceInfo[] devices = getDevicesStatic(GET_DEVICES_OUTPUTS);
|
||||||
|
for (AudioDeviceInfo device : devices) {
|
||||||
|
if (device.getType() == deviceType) {
|
||||||
|
return device;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listener registered by client to be notified upon communication audio device change.
|
||||||
|
* See {@link #setDeviceForCommunication(AudioDeviceInfo)}.
|
||||||
|
*/
|
||||||
|
public interface OnCommunicationDeviceChangedListener {
|
||||||
|
/**
|
||||||
|
* Callback method called upon communication audio device change.
|
||||||
|
* @param device the audio device selected for communication use cases
|
||||||
|
*/
|
||||||
|
void onCommunicationDeviceChanged(@Nullable AudioDeviceInfo device);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a listener for being notified of changes to the communication audio device.
|
||||||
|
* See {@link #setDeviceForCommunication(AudioDeviceInfo)}.
|
||||||
|
* @param executor
|
||||||
|
* @param listener
|
||||||
|
*/
|
||||||
|
public void addOnCommunicationDeviceChangedListener(
|
||||||
|
@NonNull @CallbackExecutor Executor executor,
|
||||||
|
@NonNull OnCommunicationDeviceChangedListener listener) {
|
||||||
|
Objects.requireNonNull(executor);
|
||||||
|
Objects.requireNonNull(listener);
|
||||||
|
synchronized (mCommDevListenerLock) {
|
||||||
|
if (hasCommDevListener(listener)) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"attempt to call addOnCommunicationDeviceChangedListener() "
|
||||||
|
+ "on a previously registered listener");
|
||||||
|
}
|
||||||
|
// lazy initialization of the list of strategy-preferred device listener
|
||||||
|
if (mCommDevListeners == null) {
|
||||||
|
mCommDevListeners = new ArrayList<>();
|
||||||
|
}
|
||||||
|
final int oldCbCount = mCommDevListeners.size();
|
||||||
|
mCommDevListeners.add(new CommDevListenerInfo(listener, executor));
|
||||||
|
if (oldCbCount == 0 && mCommDevListeners.size() > 0) {
|
||||||
|
// register binder for callbacks
|
||||||
|
if (mCommDevDispatcherStub == null) {
|
||||||
|
mCommDevDispatcherStub = new CommunicationDeviceDispatcherStub();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
getService().registerCommunicationDeviceDispatcher(mCommDevDispatcherStub);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
throw e.rethrowFromSystemServer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a previously added listener of changes to the communication audio device.
|
||||||
|
* See {@link #setDeviceForCommunication(AudioDeviceInfo)}.
|
||||||
|
* @param listener
|
||||||
|
*/
|
||||||
|
public void removeOnCommunicationDeviceChangedListener(
|
||||||
|
@NonNull OnCommunicationDeviceChangedListener listener) {
|
||||||
|
Objects.requireNonNull(listener);
|
||||||
|
synchronized (mCommDevListenerLock) {
|
||||||
|
if (!removeCommDevListener(listener)) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"attempt to call removeOnCommunicationDeviceChangedListener() "
|
||||||
|
+ "on an unregistered listener");
|
||||||
|
}
|
||||||
|
if (mCommDevListeners.size() == 0) {
|
||||||
|
// unregister binder for callbacks
|
||||||
|
try {
|
||||||
|
getService().unregisterCommunicationDeviceDispatcher(
|
||||||
|
mCommDevDispatcherStub);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
throw e.rethrowFromSystemServer();
|
||||||
|
} finally {
|
||||||
|
mCommDevDispatcherStub = null;
|
||||||
|
mCommDevListeners = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Object mCommDevListenerLock = new Object();
|
||||||
|
/**
|
||||||
|
* List of listeners for preferred device for strategy and their associated Executor.
|
||||||
|
* List is lazy-initialized on first registration
|
||||||
|
*/
|
||||||
|
@GuardedBy("mCommDevListenerLock")
|
||||||
|
private @Nullable ArrayList<CommDevListenerInfo> mCommDevListeners;
|
||||||
|
|
||||||
|
private static class CommDevListenerInfo {
|
||||||
|
final @NonNull OnCommunicationDeviceChangedListener mListener;
|
||||||
|
final @NonNull Executor mExecutor;
|
||||||
|
|
||||||
|
CommDevListenerInfo(OnCommunicationDeviceChangedListener listener, Executor exe) {
|
||||||
|
mListener = listener;
|
||||||
|
mExecutor = exe;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@GuardedBy("mCommDevListenerLock")
|
||||||
|
private CommunicationDeviceDispatcherStub mCommDevDispatcherStub;
|
||||||
|
|
||||||
|
private final class CommunicationDeviceDispatcherStub
|
||||||
|
extends ICommunicationDeviceDispatcher.Stub {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void dispatchCommunicationDeviceChanged(int portId) {
|
||||||
|
// make a shallow copy of listeners so callback is not executed under lock
|
||||||
|
final ArrayList<CommDevListenerInfo> commDevListeners;
|
||||||
|
synchronized (mCommDevListenerLock) {
|
||||||
|
if (mCommDevListeners == null || mCommDevListeners.size() == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
commDevListeners = (ArrayList<CommDevListenerInfo>) mCommDevListeners.clone();
|
||||||
|
}
|
||||||
|
AudioDeviceInfo device = getDeviceForPortId(portId, GET_DEVICES_OUTPUTS);
|
||||||
|
final long ident = Binder.clearCallingIdentity();
|
||||||
|
try {
|
||||||
|
for (CommDevListenerInfo info : commDevListeners) {
|
||||||
|
info.mExecutor.execute(() ->
|
||||||
|
info.mListener.onCommunicationDeviceChanged(device));
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
Binder.restoreCallingIdentity(ident);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@GuardedBy("mCommDevListenerLock")
|
||||||
|
private @Nullable CommDevListenerInfo getCommDevListenerInfo(
|
||||||
|
OnCommunicationDeviceChangedListener listener) {
|
||||||
|
if (mCommDevListeners == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
for (CommDevListenerInfo info : mCommDevListeners) {
|
||||||
|
if (info.mListener == listener) {
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GuardedBy("mCommDevListenerLock")
|
||||||
|
private boolean hasCommDevListener(OnCommunicationDeviceChangedListener listener) {
|
||||||
|
return getCommDevListenerInfo(listener) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GuardedBy("mCommDevListenerLock")
|
||||||
|
/**
|
||||||
|
* @return true if the listener was removed from the list
|
||||||
|
*/
|
||||||
|
private boolean removeCommDevListener(OnCommunicationDeviceChangedListener listener) {
|
||||||
|
final CommDevListenerInfo infoToRemove = getCommDevListenerInfo(listener);
|
||||||
|
if (infoToRemove != null) {
|
||||||
|
mCommDevListeners.remove(infoToRemove);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
//---------------------------------------------------------
|
//---------------------------------------------------------
|
||||||
// Inner classes
|
// Inner classes
|
||||||
//--------------------
|
//--------------------
|
||||||
|
|||||||
@@ -1712,7 +1712,7 @@ public class AudioSystem
|
|||||||
int[] types = new int[devices.size()];
|
int[] types = new int[devices.size()];
|
||||||
String[] addresses = new String[devices.size()];
|
String[] addresses = new String[devices.size()];
|
||||||
for (int i = 0; i < devices.size(); ++i) {
|
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();
|
addresses[i] = devices.get(i).getAddress();
|
||||||
}
|
}
|
||||||
return setDevicesRoleForStrategy(strategy, role, types, addresses);
|
return setDevicesRoleForStrategy(strategy, role, types, addresses);
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import android.media.IAudioFocusDispatcher;
|
|||||||
import android.media.IAudioRoutesObserver;
|
import android.media.IAudioRoutesObserver;
|
||||||
import android.media.IAudioServerStateDispatcher;
|
import android.media.IAudioServerStateDispatcher;
|
||||||
import android.media.ICapturePresetDevicesRoleDispatcher;
|
import android.media.ICapturePresetDevicesRoleDispatcher;
|
||||||
|
import android.media.ICommunicationDeviceDispatcher;
|
||||||
import android.media.IPlaybackConfigDispatcher;
|
import android.media.IPlaybackConfigDispatcher;
|
||||||
import android.media.IRecordingConfigDispatcher;
|
import android.media.IRecordingConfigDispatcher;
|
||||||
import android.media.IRingtonePlayer;
|
import android.media.IRingtonePlayer;
|
||||||
@@ -320,4 +321,13 @@ interface IAudioService {
|
|||||||
|
|
||||||
oneway void unregisterCapturePresetDevicesRoleDispatcher(
|
oneway void unregisterCapturePresetDevicesRoleDispatcher(
|
||||||
ICapturePresetDevicesRoleDispatcher dispatcher);
|
ICapturePresetDevicesRoleDispatcher dispatcher);
|
||||||
|
|
||||||
|
boolean setDeviceForCommunication(IBinder cb, int portId);
|
||||||
|
|
||||||
|
int getDeviceForCommunication();
|
||||||
|
|
||||||
|
void registerCommunicationDeviceDispatcher(ICommunicationDeviceDispatcher dispatcher);
|
||||||
|
|
||||||
|
oneway void unregisterCommunicationDeviceDispatcher(
|
||||||
|
ICommunicationDeviceDispatcher dispatcher);
|
||||||
}
|
}
|
||||||
|
|||||||
28
media/java/android/media/ICommunicationDeviceDispatcher.aidl
Normal file
28
media/java/android/media/ICommunicationDeviceDispatcher.aidl
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package android.media;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AIDL for AudioService to signal audio communication device updates.
|
||||||
|
*
|
||||||
|
* {@hide}
|
||||||
|
*/
|
||||||
|
oneway interface ICommunicationDeviceDispatcher {
|
||||||
|
|
||||||
|
void dispatchCommunicationDeviceChanged(int portId);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -16,6 +16,7 @@
|
|||||||
package com.android.server.audio;
|
package com.android.server.audio;
|
||||||
|
|
||||||
import android.annotation.NonNull;
|
import android.annotation.NonNull;
|
||||||
|
import android.annotation.Nullable;
|
||||||
import android.bluetooth.BluetoothA2dp;
|
import android.bluetooth.BluetoothA2dp;
|
||||||
import android.bluetooth.BluetoothDevice;
|
import android.bluetooth.BluetoothDevice;
|
||||||
import android.bluetooth.BluetoothHeadset;
|
import android.bluetooth.BluetoothHeadset;
|
||||||
@@ -25,20 +26,26 @@ import android.content.ContentResolver;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.media.AudioDeviceAttributes;
|
import android.media.AudioDeviceAttributes;
|
||||||
|
import android.media.AudioDeviceInfo;
|
||||||
|
import android.media.AudioManager;
|
||||||
import android.media.AudioRoutesInfo;
|
import android.media.AudioRoutesInfo;
|
||||||
import android.media.AudioSystem;
|
import android.media.AudioSystem;
|
||||||
import android.media.IAudioRoutesObserver;
|
import android.media.IAudioRoutesObserver;
|
||||||
import android.media.ICapturePresetDevicesRoleDispatcher;
|
import android.media.ICapturePresetDevicesRoleDispatcher;
|
||||||
|
import android.media.ICommunicationDeviceDispatcher;
|
||||||
import android.media.IStrategyPreferredDevicesDispatcher;
|
import android.media.IStrategyPreferredDevicesDispatcher;
|
||||||
import android.media.MediaMetrics;
|
import android.media.MediaMetrics;
|
||||||
|
import android.media.audiopolicy.AudioProductStrategy;
|
||||||
import android.os.Binder;
|
import android.os.Binder;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.os.Message;
|
import android.os.Message;
|
||||||
import android.os.PowerManager;
|
import android.os.PowerManager;
|
||||||
|
import android.os.RemoteCallbackList;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
|
import android.os.UserHandle;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.PrintWriterPrinter;
|
import android.util.PrintWriterPrinter;
|
||||||
@@ -46,8 +53,9 @@ import android.util.PrintWriterPrinter;
|
|||||||
import com.android.internal.annotations.GuardedBy;
|
import com.android.internal.annotations.GuardedBy;
|
||||||
|
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.util.ArrayList;
|
import java.util.Arrays;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.NoSuchElementException;
|
import java.util.NoSuchElementException;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@@ -72,11 +80,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
private final @NonNull Context mContext;
|
private final @NonNull Context mContext;
|
||||||
|
|
||||||
/** Forced device usage for communications sent to AudioSystem */
|
/** Forced device usage for communications sent to AudioSystem */
|
||||||
private int mForcedUseForComm;
|
private AudioDeviceAttributes mPreferredDeviceforComm;
|
||||||
/**
|
private int mCommunicationStrategyId = -1;
|
||||||
* Externally reported force device usage state returned by getters: always consistent
|
|
||||||
* with requests by setters */
|
|
||||||
private int mForcedUseForCommExt;
|
|
||||||
|
|
||||||
// Manages all connected devices, only ever accessed on the message loop
|
// Manages all connected devices, only ever accessed on the message loop
|
||||||
private final AudioDeviceInventory mDeviceInventory;
|
private final AudioDeviceInventory mDeviceInventory;
|
||||||
@@ -132,11 +137,23 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void initCommunicationStrategyId() {
|
||||||
|
List<AudioProductStrategy> strategies = AudioProductStrategy.getAudioProductStrategies();
|
||||||
|
for (AudioProductStrategy strategy : strategies) {
|
||||||
|
if (strategy.getAudioAttributesForLegacyStreamType(AudioSystem.STREAM_VOICE_CALL)
|
||||||
|
!= null) {
|
||||||
|
mCommunicationStrategyId = strategy.getId();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mCommunicationStrategyId = -1;
|
||||||
|
}
|
||||||
|
|
||||||
private void init() {
|
private void init() {
|
||||||
setupMessaging(mContext);
|
setupMessaging(mContext);
|
||||||
|
|
||||||
mForcedUseForComm = AudioSystem.FORCE_NONE;
|
mPreferredDeviceforComm = null;
|
||||||
mForcedUseForCommExt = mForcedUseForComm;
|
initCommunicationStrategyId();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*package*/ Context getContext() {
|
/*package*/ Context getContext() {
|
||||||
@@ -159,15 +176,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*package*/ void onAudioServerDied() {
|
/*package*/ void onAudioServerDied() {
|
||||||
// Restore forced usage for communications and record
|
|
||||||
synchronized (mDeviceStateLock) {
|
|
||||||
AudioSystem.setParameters(
|
|
||||||
"BT_SCO=" + (mForcedUseForComm == AudioSystem.FORCE_BT_SCO ? "on" : "off"));
|
|
||||||
onSetForceUse(AudioSystem.FOR_COMMUNICATION, mForcedUseForComm,
|
|
||||||
false /*fromA2dp*/, "onAudioServerDied");
|
|
||||||
onSetForceUse(AudioSystem.FOR_RECORD, mForcedUseForComm,
|
|
||||||
false /*fromA2dp*/, "onAudioServerDied");
|
|
||||||
}
|
|
||||||
// restore devices
|
// restore devices
|
||||||
sendMsgNoDelay(MSG_RESTORE_DEVICES, SENDMSG_REPLACE);
|
sendMsgNoDelay(MSG_RESTORE_DEVICES, SENDMSG_REPLACE);
|
||||||
}
|
}
|
||||||
@@ -219,85 +227,210 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
* Turns speakerphone on/off
|
* Turns speakerphone on/off
|
||||||
* @param on
|
* @param on
|
||||||
* @param eventSource for logging purposes
|
* @param eventSource for logging purposes
|
||||||
* @return true if speakerphone state changed
|
|
||||||
*/
|
*/
|
||||||
/*package*/ boolean setSpeakerphoneOn(IBinder cb, int pid, boolean on, String eventSource) {
|
/*package*/ void setSpeakerphoneOn(IBinder cb, int pid, boolean on, String eventSource) {
|
||||||
synchronized (mDeviceStateLock) {
|
|
||||||
if (!addSpeakerphoneClient(cb, pid, on)) {
|
if (AudioService.DEBUG_COMM_RTE) {
|
||||||
return false;
|
Log.v(TAG, "setSpeakerphoneOn, on: " + on + " pid: " + pid);
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized (mSetModeLock) {
|
||||||
|
synchronized (mDeviceStateLock) {
|
||||||
|
AudioDeviceAttributes device = null;
|
||||||
|
if (on) {
|
||||||
|
device = new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_SPEAKER, "");
|
||||||
|
} else {
|
||||||
|
CommunicationRouteClient client = getCommunicationRouteClientForPid(pid);
|
||||||
|
if (client == null || !client.requestsSpeakerphone()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setCommunicationRouteForClient(
|
||||||
|
cb, pid, device, BtHelper.SCO_MODE_UNDEFINED, eventSource);
|
||||||
}
|
}
|
||||||
if (on) {
|
|
||||||
// Cancel BT SCO ON request by this same client: speakerphone and BT SCO routes
|
|
||||||
// are mutually exclusive.
|
|
||||||
// See symmetrical operation for startBluetoothScoForClient_Sync().
|
|
||||||
mBtHelper.stopBluetoothScoForPid(pid);
|
|
||||||
}
|
|
||||||
final boolean wasOn = isSpeakerphoneOn();
|
|
||||||
updateSpeakerphoneOn(eventSource);
|
|
||||||
return (wasOn != isSpeakerphoneOn());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Turns speakerphone off for a given pid and update speakerphone state.
|
* Select device for use for communication use cases.
|
||||||
* @param pid
|
* @param cb Client binder for death detection
|
||||||
|
* @param pid Client pid
|
||||||
|
* @param device Device selected or null to unselect.
|
||||||
|
* @param eventSource for logging purposes
|
||||||
*/
|
*/
|
||||||
|
/*package*/ boolean setDeviceForCommunication(
|
||||||
|
IBinder cb, int pid, AudioDeviceInfo device, String eventSource) {
|
||||||
|
|
||||||
|
if (AudioService.DEBUG_COMM_RTE) {
|
||||||
|
Log.v(TAG, "setDeviceForCommunication, device: " + device + ", pid: " + pid);
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized (mSetModeLock) {
|
||||||
|
synchronized (mDeviceStateLock) {
|
||||||
|
AudioDeviceAttributes deviceAttr = null;
|
||||||
|
if (device != null) {
|
||||||
|
deviceAttr = new AudioDeviceAttributes(device);
|
||||||
|
} else {
|
||||||
|
CommunicationRouteClient client = getCommunicationRouteClientForPid(pid);
|
||||||
|
if (client == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setCommunicationRouteForClient(
|
||||||
|
cb, pid, deviceAttr, BtHelper.SCO_MODE_UNDEFINED, eventSource);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@GuardedBy("mDeviceStateLock")
|
@GuardedBy("mDeviceStateLock")
|
||||||
private void setSpeakerphoneOffForPid(int pid) {
|
/*package*/ void setCommunicationRouteForClient(
|
||||||
SpeakerphoneClient client = getSpeakerphoneClientForPid(pid);
|
IBinder cb, int pid, AudioDeviceAttributes device,
|
||||||
|
int scoAudioMode, String eventSource) {
|
||||||
|
|
||||||
|
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();
|
||||||
|
CommunicationRouteClient client;
|
||||||
|
|
||||||
|
|
||||||
|
// Save previous client route in case of failure to start BT SCO audio
|
||||||
|
AudioDeviceAttributes prevClientDevice = null;
|
||||||
|
client = getCommunicationRouteClientForPid(pid);
|
||||||
|
if (client != null) {
|
||||||
|
prevClientDevice = client.getDevice();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (device != null) {
|
||||||
|
client = addCommunicationRouteClient(cb, pid, device);
|
||||||
|
if (client == null) {
|
||||||
|
Log.w(TAG, "setCommunicationRouteForClient: could not add client for pid: "
|
||||||
|
+ pid + " and device: " + device);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
client = removeCommunicationRouteClient(cb, true);
|
||||||
|
}
|
||||||
if (client == null) {
|
if (client == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
client.unregisterDeathRecipient();
|
|
||||||
mSpeakerphoneClients.remove(client);
|
|
||||||
final String eventSource = new StringBuilder("setSpeakerphoneOffForPid(")
|
|
||||||
.append(pid).append(")").toString();
|
|
||||||
updateSpeakerphoneOn(eventSource);
|
|
||||||
}
|
|
||||||
|
|
||||||
@GuardedBy("mDeviceStateLock")
|
boolean isBtScoRequested = isBluetoothScoRequested();
|
||||||
private void updateSpeakerphoneOn(String eventSource) {
|
if (isBtScoRequested && !wasBtScoRequested) {
|
||||||
if (isSpeakerphoneOnRequested()) {
|
if (!mBtHelper.startBluetoothSco(scoAudioMode, eventSource)) {
|
||||||
if (mForcedUseForComm == AudioSystem.FORCE_BT_SCO) {
|
Log.w(TAG, "setCommunicationRouteForClient: failure to start BT SCO for pid: "
|
||||||
setForceUse_Async(AudioSystem.FOR_RECORD, AudioSystem.FORCE_NONE, eventSource);
|
+ pid);
|
||||||
|
// clean up or restore previous client selection
|
||||||
|
if (prevClientDevice != null) {
|
||||||
|
addCommunicationRouteClient(cb, pid, prevClientDevice);
|
||||||
|
} else {
|
||||||
|
removeCommunicationRouteClient(cb, true);
|
||||||
|
}
|
||||||
|
postBroadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
|
||||||
}
|
}
|
||||||
mForcedUseForComm = AudioSystem.FORCE_SPEAKER;
|
} else if (!isBtScoRequested && wasBtScoRequested) {
|
||||||
} else if (mForcedUseForComm == AudioSystem.FORCE_SPEAKER) {
|
mBtHelper.stopBluetoothSco(eventSource);
|
||||||
if (mBtHelper.isBluetoothScoOn()) {
|
}
|
||||||
mForcedUseForComm = AudioSystem.FORCE_BT_SCO;
|
|
||||||
setForceUse_Async(
|
if (wasSpeakerphoneRequested != isSpeakerphoneRequested()) {
|
||||||
AudioSystem.FOR_RECORD, AudioSystem.FORCE_BT_SCO, eventSource);
|
try {
|
||||||
} else {
|
mContext.sendBroadcastAsUser(
|
||||||
mForcedUseForComm = AudioSystem.FORCE_NONE;
|
new Intent(AudioManager.ACTION_SPEAKERPHONE_STATE_CHANGED)
|
||||||
|
.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), UserHandle.ALL);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.w(TAG, "failed to broadcast ACTION_SPEAKERPHONE_STATE_CHANGED: " + e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mForcedUseForCommExt = mForcedUseForComm;
|
|
||||||
setForceUse_Async(AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, eventSource);
|
sendLMsgNoDelay(MSG_L_UPDATE_COMMUNICATION_ROUTE, SENDMSG_QUEUE, eventSource);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns if speakerphone is requested ON or OFF.
|
* Returns the device currently requested for communication use case.
|
||||||
* If the current audio mode owner is in the speakerphone client list, use this preference.
|
* If the current audio mode owner is in the communication route client list,
|
||||||
|
* use this preference.
|
||||||
* Otherwise use first client's preference (first client corresponds to latest request).
|
* Otherwise use first client's preference (first client corresponds to latest request).
|
||||||
* Speakerphone is requested OFF if no client is in the list.
|
* null is returned if no client is in the list.
|
||||||
* @return true if speakerphone is requested ON, false otherwise
|
* @return AudioDeviceAttributes the requested device for communication.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@GuardedBy("mDeviceStateLock")
|
@GuardedBy("mDeviceStateLock")
|
||||||
private boolean isSpeakerphoneOnRequested() {
|
private AudioDeviceAttributes requestedCommunicationDevice() {
|
||||||
if (mSpeakerphoneClients.isEmpty()) {
|
AudioDeviceAttributes device = null;
|
||||||
return false;
|
for (CommunicationRouteClient cl : mCommunicationRouteClients) {
|
||||||
}
|
|
||||||
for (SpeakerphoneClient cl : mSpeakerphoneClients) {
|
|
||||||
if (cl.getPid() == mModeOwnerPid) {
|
if (cl.getPid() == mModeOwnerPid) {
|
||||||
return cl.isOn();
|
device = cl.getDevice();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return mSpeakerphoneClients.get(0).isOn();
|
if (!mCommunicationRouteClients.isEmpty() && mModeOwnerPid == 0) {
|
||||||
|
device = mCommunicationRouteClients.get(0).getDevice();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (AudioService.DEBUG_COMM_RTE) {
|
||||||
|
Log.v(TAG, "requestedCommunicationDevice, device: "
|
||||||
|
+ device + " mode owner pid: " + mModeOwnerPid);
|
||||||
|
}
|
||||||
|
return device;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*package*/ boolean isSpeakerphoneOn() {
|
/**
|
||||||
|
* Returns the device currently requested for communication use case.
|
||||||
|
* @return AudioDeviceInfo the requested device for communication.
|
||||||
|
*/
|
||||||
|
AudioDeviceInfo getDeviceForCommunication() {
|
||||||
synchronized (mDeviceStateLock) {
|
synchronized (mDeviceStateLock) {
|
||||||
return (mForcedUseForCommExt == AudioSystem.FORCE_SPEAKER);
|
AudioDeviceAttributes device = requestedCommunicationDevice();
|
||||||
|
if (device == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return AudioManager.getDeviceInfoFromType(device.getType());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper method on top of requestedCommunicationDevice() indicating if
|
||||||
|
* speakerphone ON is currently requested or not.
|
||||||
|
* @return true if speakerphone ON requested, false otherwise.
|
||||||
|
*/
|
||||||
|
|
||||||
|
private boolean isSpeakerphoneRequested() {
|
||||||
|
synchronized (mDeviceStateLock) {
|
||||||
|
AudioDeviceAttributes device = requestedCommunicationDevice();
|
||||||
|
return device != null
|
||||||
|
&& device.getType()
|
||||||
|
== AudioDeviceInfo.TYPE_BUILTIN_SPEAKER;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if active route selection for communication is speakerphone.
|
||||||
|
* @return true if speakerphone is active, false otherwise.
|
||||||
|
*/
|
||||||
|
/*package*/ boolean isSpeakerphoneOn() {
|
||||||
|
AudioDeviceAttributes device = getPreferredDeviceForComm();
|
||||||
|
if (device == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return device.getInternalType() == AudioSystem.DEVICE_OUT_SPEAKER;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper method on top of requestedCommunicationDevice() indicating if
|
||||||
|
* Bluetooth SCO ON is currently requested or not.
|
||||||
|
* @return true if Bluetooth SCO ON is requested, false otherwise.
|
||||||
|
*/
|
||||||
|
/*package*/ boolean isBluetoothScoRequested() {
|
||||||
|
synchronized (mDeviceStateLock) {
|
||||||
|
AudioDeviceAttributes device = requestedCommunicationDevice();
|
||||||
|
return device != null
|
||||||
|
&& device.getType()
|
||||||
|
== AudioDeviceInfo.TYPE_BLUETOOTH_SCO;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -348,7 +481,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*package*/ void postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
|
/*package*/ void postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
|
||||||
@NonNull BluetoothDevice device, @AudioService.BtProfileConnectionState int state,
|
@NonNull BluetoothDevice device, @AudioService.BtProfileConnectionState int state,
|
||||||
int profile, boolean suppressNoisyIntent, int a2dpVolume) {
|
int profile, boolean suppressNoisyIntent, int a2dpVolume) {
|
||||||
@@ -431,42 +563,32 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
sendLMsgNoDelay(MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT, SENDMSG_QUEUE, info);
|
sendLMsgNoDelay(MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT, SENDMSG_QUEUE, info);
|
||||||
}
|
}
|
||||||
|
|
||||||
// never called by system components
|
|
||||||
/*package*/ void setBluetoothScoOnByApp(boolean on) {
|
|
||||||
synchronized (mDeviceStateLock) {
|
|
||||||
mForcedUseForCommExt = on ? AudioSystem.FORCE_BT_SCO : AudioSystem.FORCE_NONE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*package*/ boolean isBluetoothScoOnForApp() {
|
/**
|
||||||
synchronized (mDeviceStateLock) {
|
* Current Bluetooth SCO audio active state indicated by BtHelper via setBluetoothScoOn().
|
||||||
return mForcedUseForCommExt == AudioSystem.FORCE_BT_SCO;
|
*/
|
||||||
}
|
private boolean mBluetoothScoOn;
|
||||||
}
|
|
||||||
|
|
||||||
/*package*/ void setBluetoothScoOn(boolean on, String eventSource) {
|
/*package*/ void setBluetoothScoOn(boolean on, String eventSource) {
|
||||||
//Log.i(TAG, "setBluetoothScoOn: " + on + " " + eventSource);
|
if (AudioService.DEBUG_COMM_RTE) {
|
||||||
synchronized (mDeviceStateLock) {
|
Log.v(TAG, "setBluetoothScoOn: " + on + " " + eventSource);
|
||||||
if (on) {
|
|
||||||
// do not accept SCO ON if SCO audio is not connected
|
|
||||||
if (!mBtHelper.isBluetoothScoOn()) {
|
|
||||||
mForcedUseForCommExt = AudioSystem.FORCE_BT_SCO;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
mForcedUseForComm = AudioSystem.FORCE_BT_SCO;
|
|
||||||
} else if (mForcedUseForComm == AudioSystem.FORCE_BT_SCO) {
|
|
||||||
mForcedUseForComm = isSpeakerphoneOnRequested()
|
|
||||||
? AudioSystem.FORCE_SPEAKER : AudioSystem.FORCE_NONE;
|
|
||||||
}
|
|
||||||
mForcedUseForCommExt = mForcedUseForComm;
|
|
||||||
AudioSystem.setParameters("BT_SCO=" + (on ? "on" : "off"));
|
|
||||||
sendIILMsgNoDelay(MSG_IIL_SET_FORCE_USE, SENDMSG_QUEUE,
|
|
||||||
AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, eventSource);
|
|
||||||
sendIILMsgNoDelay(MSG_IIL_SET_FORCE_USE, SENDMSG_QUEUE,
|
|
||||||
AudioSystem.FOR_RECORD, mForcedUseForComm, eventSource);
|
|
||||||
}
|
}
|
||||||
// Un-mute ringtone stream volume
|
synchronized (mDeviceStateLock) {
|
||||||
mAudioService.postUpdateRingerModeServiceInt();
|
mBluetoothScoOn = on;
|
||||||
|
sendLMsgNoDelay(MSG_L_UPDATE_COMMUNICATION_ROUTE, SENDMSG_QUEUE, eventSource);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if active route selection for communication is Bluetooth SCO.
|
||||||
|
* @return true if Bluetooth SCO is active , false otherwise.
|
||||||
|
*/
|
||||||
|
/*package*/ boolean isBluetoothScoOn() {
|
||||||
|
AudioDeviceAttributes device = getPreferredDeviceForComm();
|
||||||
|
if (device == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return AudioSystem.DEVICE_OUT_ALL_SCO_SET.contains(device.getInternalType());
|
||||||
}
|
}
|
||||||
|
|
||||||
/*package*/ AudioRoutesInfo startWatchingRoutes(IAudioRoutesObserver observer) {
|
/*package*/ AudioRoutesInfo startWatchingRoutes(IAudioRoutesObserver observer) {
|
||||||
@@ -509,22 +631,38 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
sendLMsgNoDelay(MSG_L_A2DP_DEVICE_CONFIG_CHANGE, SENDMSG_QUEUE, device);
|
sendLMsgNoDelay(MSG_L_A2DP_DEVICE_CONFIG_CHANGE, SENDMSG_QUEUE, device);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GuardedBy("mSetModeLock")
|
/*package*/ void startBluetoothScoForClient(IBinder cb, int pid, int scoAudioMode,
|
||||||
/*package*/ void startBluetoothScoForClient_Sync(IBinder cb, int scoAudioMode,
|
|
||||||
@NonNull String eventSource) {
|
@NonNull String eventSource) {
|
||||||
synchronized (mDeviceStateLock) {
|
|
||||||
// Cancel speakerphone ON request by this same client: speakerphone and BT SCO routes
|
if (AudioService.DEBUG_COMM_RTE) {
|
||||||
// are mutually exclusive.
|
Log.v(TAG, "startBluetoothScoForClient_Sync, pid: " + pid);
|
||||||
// See symmetrical operation for setSpeakerphoneOn(true).
|
}
|
||||||
setSpeakerphoneOffForPid(Binder.getCallingPid());
|
|
||||||
mBtHelper.startBluetoothScoForClient(cb, scoAudioMode, eventSource);
|
synchronized (mSetModeLock) {
|
||||||
|
synchronized (mDeviceStateLock) {
|
||||||
|
AudioDeviceAttributes device =
|
||||||
|
new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_BLUETOOTH_SCO, "");
|
||||||
|
setCommunicationRouteForClient(cb, pid, device, scoAudioMode, eventSource);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@GuardedBy("mSetModeLock")
|
/*package*/ void stopBluetoothScoForClient(
|
||||||
/*package*/ void stopBluetoothScoForClient_Sync(IBinder cb, @NonNull String eventSource) {
|
IBinder cb, int pid, @NonNull String eventSource) {
|
||||||
synchronized (mDeviceStateLock) {
|
|
||||||
mBtHelper.stopBluetoothScoForClient(cb, eventSource);
|
if (AudioService.DEBUG_COMM_RTE) {
|
||||||
|
Log.v(TAG, "stopBluetoothScoForClient_Sync, pid: " + pid);
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized (mSetModeLock) {
|
||||||
|
synchronized (mDeviceStateLock) {
|
||||||
|
CommunicationRouteClient client = getCommunicationRouteClientForPid(pid);
|
||||||
|
if (client == null || !client.requestsBluetoothSco()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setCommunicationRouteForClient(
|
||||||
|
cb, pid, null, BtHelper.SCO_MODE_UNDEFINED, eventSource);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -533,10 +671,19 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
return mDeviceInventory.setPreferredDevicesForStrategySync(strategy, devices);
|
return mDeviceInventory.setPreferredDevicesForStrategySync(strategy, devices);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*package*/ void postSetPreferredDevicesForStrategy(int strategy,
|
||||||
|
@NonNull List<AudioDeviceAttributes> devices) {
|
||||||
|
sendILMsgNoDelay(MSG_IL_SET_PREF_DEVICES_FOR_STRATEGY, SENDMSG_REPLACE, strategy, devices);
|
||||||
|
}
|
||||||
|
|
||||||
/*package*/ int removePreferredDevicesForStrategySync(int strategy) {
|
/*package*/ int removePreferredDevicesForStrategySync(int strategy) {
|
||||||
return mDeviceInventory.removePreferredDevicesForStrategySync(strategy);
|
return mDeviceInventory.removePreferredDevicesForStrategySync(strategy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*package*/ void postRemovePreferredDevicesForStrategy(int strategy) {
|
||||||
|
sendIMsgNoDelay(MSG_I_REMOVE_PREF_DEVICES_FOR_STRATEGY, SENDMSG_REPLACE, strategy);
|
||||||
|
}
|
||||||
|
|
||||||
/*package*/ void registerStrategyPreferredDevicesDispatcher(
|
/*package*/ void registerStrategyPreferredDevicesDispatcher(
|
||||||
@NonNull IStrategyPreferredDevicesDispatcher dispatcher) {
|
@NonNull IStrategyPreferredDevicesDispatcher dispatcher) {
|
||||||
mDeviceInventory.registerStrategyPreferredDevicesDispatcher(dispatcher);
|
mDeviceInventory.registerStrategyPreferredDevicesDispatcher(dispatcher);
|
||||||
@@ -566,6 +713,45 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
mDeviceInventory.unregisterCapturePresetDevicesRoleDispatcher(dispatcher);
|
mDeviceInventory.unregisterCapturePresetDevicesRoleDispatcher(dispatcher);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*package*/ void registerCommunicationDeviceDispatcher(
|
||||||
|
@NonNull ICommunicationDeviceDispatcher dispatcher) {
|
||||||
|
mCommDevDispatchers.register(dispatcher);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*package*/ void unregisterCommunicationDeviceDispatcher(
|
||||||
|
@NonNull ICommunicationDeviceDispatcher dispatcher) {
|
||||||
|
mCommDevDispatchers.unregister(dispatcher);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Monitoring of communication device
|
||||||
|
final RemoteCallbackList<ICommunicationDeviceDispatcher> mCommDevDispatchers =
|
||||||
|
new RemoteCallbackList<ICommunicationDeviceDispatcher>();
|
||||||
|
|
||||||
|
// portId of the device currently selected for communication: avoids broadcasting changes
|
||||||
|
// when same communication route is applied
|
||||||
|
@GuardedBy("mDeviceStateLock")
|
||||||
|
int mCurCommunicationPortId = -1;
|
||||||
|
|
||||||
|
@GuardedBy("mDeviceStateLock")
|
||||||
|
private void dispatchCommunicationDevice() {
|
||||||
|
AudioDeviceInfo device = getDeviceForCommunication();
|
||||||
|
int portId = (getDeviceForCommunication() == null) ? 0 : device.getId();
|
||||||
|
if (portId == mCurCommunicationPortId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mCurCommunicationPortId = portId;
|
||||||
|
|
||||||
|
final int nbDispatchers = mCommDevDispatchers.beginBroadcast();
|
||||||
|
for (int i = 0; i < nbDispatchers; i++) {
|
||||||
|
try {
|
||||||
|
mCommDevDispatchers.getBroadcastItem(i)
|
||||||
|
.dispatchCommunicationDeviceChanged(portId);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mCommDevDispatchers.finishBroadcast();
|
||||||
|
}
|
||||||
|
|
||||||
//---------------------------------------------------------------------
|
//---------------------------------------------------------------------
|
||||||
// Communication with (to) AudioService
|
// Communication with (to) AudioService
|
||||||
//TODO check whether the AudioService methods are candidates to move here
|
//TODO check whether the AudioService methods are candidates to move here
|
||||||
@@ -696,12 +882,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
hearingAidProfile);
|
hearingAidProfile);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*package*/ void postScoClientDied(Object obj) {
|
/*package*/ void postCommunicationRouteClientDied(CommunicationRouteClient client) {
|
||||||
sendLMsgNoDelay(MSG_L_SCOCLIENT_DIED, SENDMSG_QUEUE, obj);
|
sendLMsgNoDelay(MSG_L_COMMUNICATION_ROUTE_CLIENT_DIED, SENDMSG_QUEUE, client);
|
||||||
}
|
|
||||||
|
|
||||||
/*package*/ void postSpeakerphoneClientDied(Object obj) {
|
|
||||||
sendLMsgNoDelay(MSG_L_SPEAKERPHONE_CLIENT_DIED, SENDMSG_QUEUE, obj);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*package*/ void postSaveSetPreferredDevicesForStrategy(int strategy,
|
/*package*/ void postSaveSetPreferredDevicesForStrategy(int strategy,
|
||||||
@@ -821,15 +1003,17 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
|
|
||||||
mDeviceInventory.dump(pw, prefix);
|
mDeviceInventory.dump(pw, prefix);
|
||||||
|
|
||||||
pw.println("\n" + prefix + "mForcedUseForComm: "
|
pw.println("\n" + prefix + "Communication route clients:");
|
||||||
+ AudioSystem.forceUseConfigToString(mForcedUseForComm));
|
mCommunicationRouteClients.forEach((cl) -> {
|
||||||
pw.println(prefix + "mForcedUseForCommExt: "
|
pw.println(" " + prefix + "pid: " + cl.getPid() + " device: "
|
||||||
+ AudioSystem.forceUseConfigToString(mForcedUseForCommExt));
|
+ cl.getDevice() + " cb: " + cl.getBinder()); });
|
||||||
pw.println(prefix + "mModeOwnerPid: " + mModeOwnerPid);
|
|
||||||
pw.println(prefix + "Speakerphone clients:");
|
pw.println("\n" + prefix + "mPreferredDeviceforComm: "
|
||||||
mSpeakerphoneClients.forEach((cl) -> {
|
+ mPreferredDeviceforComm);
|
||||||
pw.println(" " + prefix + "pid: " + cl.getPid() + " on: "
|
pw.println(prefix + "mCommunicationStrategyId: "
|
||||||
+ cl.isOn() + " cb: " + cl.getBinder()); });
|
+ mCommunicationStrategyId);
|
||||||
|
|
||||||
|
pw.println("\n" + prefix + "mModeOwnerPid: " + mModeOwnerPid);
|
||||||
|
|
||||||
mBtHelper.dump(pw, prefix);
|
mBtHelper.dump(pw, prefix);
|
||||||
}
|
}
|
||||||
@@ -852,6 +1036,11 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
.set(MediaMetrics.Property.FORCE_USE_MODE,
|
.set(MediaMetrics.Property.FORCE_USE_MODE,
|
||||||
AudioSystem.forceUseConfigToString(config))
|
AudioSystem.forceUseConfigToString(config))
|
||||||
.record();
|
.record();
|
||||||
|
|
||||||
|
if (AudioService.DEBUG_COMM_RTE) {
|
||||||
|
Log.v(TAG, "onSetForceUse(useCase<" + useCase + ">, config<" + config + ">, fromA2dp<"
|
||||||
|
+ fromA2dp + ">, eventSource<" + eventSource + ">)");
|
||||||
|
}
|
||||||
AudioSystem.setForceUse(useCase, config);
|
AudioSystem.setForceUse(useCase, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -917,9 +1106,13 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
public void handleMessage(Message msg) {
|
public void handleMessage(Message msg) {
|
||||||
switch (msg.what) {
|
switch (msg.what) {
|
||||||
case MSG_RESTORE_DEVICES:
|
case MSG_RESTORE_DEVICES:
|
||||||
synchronized (mDeviceStateLock) {
|
synchronized (mSetModeLock) {
|
||||||
mDeviceInventory.onRestoreDevices();
|
synchronized (mDeviceStateLock) {
|
||||||
mBtHelper.onAudioServerDiedRestoreA2dp();
|
initCommunicationStrategyId();
|
||||||
|
mDeviceInventory.onRestoreDevices();
|
||||||
|
mBtHelper.onAudioServerDiedRestoreA2dp();
|
||||||
|
onUpdateCommunicationRoute("MSG_RESTORE_DEVICES");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE:
|
case MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE:
|
||||||
@@ -1009,25 +1202,24 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
if (mModeOwnerPid != msg.arg1) {
|
if (mModeOwnerPid != msg.arg1) {
|
||||||
mModeOwnerPid = msg.arg1;
|
mModeOwnerPid = msg.arg1;
|
||||||
if (msg.arg2 != AudioSystem.MODE_RINGTONE) {
|
if (msg.arg2 != AudioSystem.MODE_RINGTONE) {
|
||||||
updateSpeakerphoneOn("setNewModeOwner");
|
onUpdateCommunicationRoute("setNewModeOwner");
|
||||||
}
|
|
||||||
if (mModeOwnerPid != 0) {
|
|
||||||
mBtHelper.disconnectBluetoothSco(mModeOwnerPid);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case MSG_L_SCOCLIENT_DIED:
|
case MSG_L_COMMUNICATION_ROUTE_CLIENT_DIED:
|
||||||
synchronized (mSetModeLock) {
|
synchronized (mSetModeLock) {
|
||||||
synchronized (mDeviceStateLock) {
|
synchronized (mDeviceStateLock) {
|
||||||
mBtHelper.scoClientDied(msg.obj);
|
onCommunicationRouteClientDied((CommunicationRouteClient) msg.obj);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case MSG_L_SPEAKERPHONE_CLIENT_DIED:
|
case MSG_L_UPDATE_COMMUNICATION_ROUTE:
|
||||||
synchronized (mDeviceStateLock) {
|
synchronized (mSetModeLock) {
|
||||||
speakerphoneClientDied(msg.obj);
|
synchronized (mDeviceStateLock) {
|
||||||
|
onUpdateCommunicationRoute((String) msg.obj);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case MSG_TOGGLE_HDMI:
|
case MSG_TOGGLE_HDMI:
|
||||||
@@ -1126,6 +1318,17 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
final int strategy = msg.arg1;
|
final int strategy = msg.arg1;
|
||||||
mDeviceInventory.onSaveRemovePreferredDevices(strategy);
|
mDeviceInventory.onSaveRemovePreferredDevices(strategy);
|
||||||
} break;
|
} break;
|
||||||
|
case MSG_IL_SET_PREF_DEVICES_FOR_STRATEGY: {
|
||||||
|
final int strategy = msg.arg1;
|
||||||
|
final List<AudioDeviceAttributes> devices =
|
||||||
|
(List<AudioDeviceAttributes>) 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:
|
case MSG_CHECK_MUTE_MUSIC:
|
||||||
checkMessagesMuteMusic(0);
|
checkMessagesMuteMusic(0);
|
||||||
break;
|
break;
|
||||||
@@ -1207,18 +1410,19 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
// process external command to (dis)connect a hearing aid device
|
// process external command to (dis)connect a hearing aid device
|
||||||
private static final int MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT = 31;
|
private static final int MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT = 31;
|
||||||
|
|
||||||
// a ScoClient died in BtHelper
|
private static final int MSG_IL_SAVE_PREF_DEVICES_FOR_STRATEGY = 32;
|
||||||
private static final int MSG_L_SCOCLIENT_DIED = 32;
|
private static final int MSG_I_SAVE_REMOVE_PREF_DEVICES_FOR_STRATEGY = 33;
|
||||||
private static final int MSG_IL_SAVE_PREF_DEVICES_FOR_STRATEGY = 33;
|
|
||||||
private static final int MSG_I_SAVE_REMOVE_PREF_DEVICES_FOR_STRATEGY = 34;
|
|
||||||
|
|
||||||
private static final int MSG_L_SPEAKERPHONE_CLIENT_DIED = 35;
|
private static final int MSG_L_COMMUNICATION_ROUTE_CLIENT_DIED = 34;
|
||||||
private static final int MSG_CHECK_MUTE_MUSIC = 36;
|
private static final int MSG_CHECK_MUTE_MUSIC = 35;
|
||||||
private static final int MSG_REPORT_NEW_ROUTES_A2DP = 37;
|
private static final int MSG_REPORT_NEW_ROUTES_A2DP = 36;
|
||||||
|
|
||||||
private static final int MSG_IL_SAVE_PREF_DEVICES_FOR_CAPTURE_PRESET = 38;
|
private static final int MSG_IL_SAVE_PREF_DEVICES_FOR_CAPTURE_PRESET = 37;
|
||||||
private static final int MSG_I_SAVE_CLEAR_PREF_DEVICES_FOR_CAPTURE_PRESET = 39;
|
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) {
|
private static boolean isMessageHandledUnderWakelock(int msgId) {
|
||||||
switch(msgId) {
|
switch(msgId) {
|
||||||
@@ -1372,14 +1576,20 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class SpeakerphoneClient implements IBinder.DeathRecipient {
|
// List of applications requesting a specific route for communication.
|
||||||
|
@GuardedBy("mDeviceStateLock")
|
||||||
|
private final @NonNull LinkedList<CommunicationRouteClient> mCommunicationRouteClients =
|
||||||
|
new LinkedList<CommunicationRouteClient>();
|
||||||
|
|
||||||
|
private class CommunicationRouteClient implements IBinder.DeathRecipient {
|
||||||
private final IBinder mCb;
|
private final IBinder mCb;
|
||||||
private final int mPid;
|
private final int mPid;
|
||||||
private final boolean mOn;
|
private AudioDeviceAttributes mDevice;
|
||||||
SpeakerphoneClient(IBinder cb, int pid, boolean on) {
|
|
||||||
|
CommunicationRouteClient(IBinder cb, int pid, AudioDeviceAttributes device) {
|
||||||
mCb = cb;
|
mCb = cb;
|
||||||
mPid = pid;
|
mPid = pid;
|
||||||
mOn = on;
|
mDevice = device;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean registerDeathRecipient() {
|
public boolean registerDeathRecipient() {
|
||||||
@@ -1388,7 +1598,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
mCb.linkToDeath(this, 0);
|
mCb.linkToDeath(this, 0);
|
||||||
status = true;
|
status = true;
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
Log.w(TAG, "SpeakerphoneClient could not link to " + mCb + " binder death");
|
Log.w(TAG, "CommunicationRouteClient could not link to " + mCb + " binder death");
|
||||||
}
|
}
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
@@ -1397,13 +1607,13 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
try {
|
try {
|
||||||
mCb.unlinkToDeath(this, 0);
|
mCb.unlinkToDeath(this, 0);
|
||||||
} catch (NoSuchElementException e) {
|
} catch (NoSuchElementException e) {
|
||||||
Log.w(TAG, "SpeakerphoneClient could not not unregistered to binder");
|
Log.w(TAG, "CommunicationRouteClient could not not unregistered to binder");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void binderDied() {
|
public void binderDied() {
|
||||||
postSpeakerphoneClientDied(this);
|
postCommunicationRouteClientDied(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
IBinder getBinder() {
|
IBinder getBinder() {
|
||||||
@@ -1414,29 +1624,103 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
return mPid;
|
return mPid;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isOn() {
|
AudioDeviceAttributes getDevice() {
|
||||||
return mOn;
|
return mDevice;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean requestsBluetoothSco() {
|
||||||
|
return mDevice != null
|
||||||
|
&& mDevice.getType()
|
||||||
|
== AudioDeviceInfo.TYPE_BLUETOOTH_SCO;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean requestsSpeakerphone() {
|
||||||
|
return mDevice != null
|
||||||
|
&& mDevice.getType()
|
||||||
|
== AudioDeviceInfo.TYPE_BUILTIN_SPEAKER;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @GuardedBy("mSetModeLock")
|
||||||
@GuardedBy("mDeviceStateLock")
|
@GuardedBy("mDeviceStateLock")
|
||||||
private void speakerphoneClientDied(Object obj) {
|
private void onCommunicationRouteClientDied(CommunicationRouteClient client) {
|
||||||
if (obj == null) {
|
if (client == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Log.w(TAG, "Speaker client died");
|
Log.w(TAG, "Speaker client died");
|
||||||
if (removeSpeakerphoneClient(((SpeakerphoneClient) obj).getBinder(), false) != null) {
|
if (removeCommunicationRouteClient(client.getBinder(), false)
|
||||||
updateSpeakerphoneOn("speakerphoneClientDied");
|
!= null) {
|
||||||
|
onUpdateCommunicationRoute("onCommunicationRouteClientDied");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private SpeakerphoneClient removeSpeakerphoneClient(IBinder cb, boolean unregister) {
|
/**
|
||||||
for (SpeakerphoneClient cl : mSpeakerphoneClients) {
|
* Determines which forced usage for communication should be sent to audio policy manager
|
||||||
|
* as a function of current SCO audio activation state and active communication route requests.
|
||||||
|
* SCO audio state has the highest priority as it can result from external activation by
|
||||||
|
* telephony service.
|
||||||
|
* @return selected forced usage for communication.
|
||||||
|
*/
|
||||||
|
@GuardedBy("mDeviceStateLock")
|
||||||
|
@Nullable private AudioDeviceAttributes getPreferredDeviceForComm() {
|
||||||
|
boolean btSCoOn = mBluetoothScoOn && mBtHelper.isBluetoothScoOn();
|
||||||
|
if (btSCoOn) {
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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 device;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures audio policy manager and audio HAL according to active communication route.
|
||||||
|
* Always called from message Handler.
|
||||||
|
*/
|
||||||
|
// @GuardedBy("mSetModeLock")
|
||||||
|
@GuardedBy("mDeviceStateLock")
|
||||||
|
private void onUpdateCommunicationRoute(String eventSource) {
|
||||||
|
mPreferredDeviceforComm = getPreferredDeviceForComm();
|
||||||
|
if (AudioService.DEBUG_COMM_RTE) {
|
||||||
|
Log.v(TAG, "onUpdateCommunicationRoute, mPreferredDeviceforComm: "
|
||||||
|
+ mPreferredDeviceforComm + " eventSource: " + eventSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mPreferredDeviceforComm == null
|
||||||
|
|| !AudioSystem.DEVICE_OUT_ALL_SCO_SET.contains(
|
||||||
|
mPreferredDeviceforComm.getInternalType())) {
|
||||||
|
AudioSystem.setParameters("BT_SCO=off");
|
||||||
|
} else {
|
||||||
|
AudioSystem.setParameters("BT_SCO=on");
|
||||||
|
}
|
||||||
|
if (mPreferredDeviceforComm == null) {
|
||||||
|
postRemovePreferredDevicesForStrategy(mCommunicationStrategyId);
|
||||||
|
} else {
|
||||||
|
postSetPreferredDevicesForStrategy(
|
||||||
|
mCommunicationStrategyId, Arrays.asList(mPreferredDeviceforComm));
|
||||||
|
}
|
||||||
|
mAudioService.postUpdateRingerModeServiceInt();
|
||||||
|
dispatchCommunicationDevice();
|
||||||
|
}
|
||||||
|
|
||||||
|
private CommunicationRouteClient removeCommunicationRouteClient(
|
||||||
|
IBinder cb, boolean unregister) {
|
||||||
|
for (CommunicationRouteClient cl : mCommunicationRouteClients) {
|
||||||
if (cl.getBinder() == cb) {
|
if (cl.getBinder() == cb) {
|
||||||
if (unregister) {
|
if (unregister) {
|
||||||
cl.unregisterDeathRecipient();
|
cl.unregisterDeathRecipient();
|
||||||
}
|
}
|
||||||
mSpeakerphoneClients.remove(cl);
|
mCommunicationRouteClients.remove(cl);
|
||||||
return cl;
|
return cl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1444,30 +1728,25 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
}
|
}
|
||||||
|
|
||||||
@GuardedBy("mDeviceStateLock")
|
@GuardedBy("mDeviceStateLock")
|
||||||
private boolean addSpeakerphoneClient(IBinder cb, int pid, boolean on) {
|
private CommunicationRouteClient addCommunicationRouteClient(
|
||||||
|
IBinder cb, int pid, AudioDeviceAttributes device) {
|
||||||
// always insert new request at first position
|
// always insert new request at first position
|
||||||
removeSpeakerphoneClient(cb, true);
|
removeCommunicationRouteClient(cb, true);
|
||||||
SpeakerphoneClient client = new SpeakerphoneClient(cb, pid, on);
|
CommunicationRouteClient client = new CommunicationRouteClient(cb, pid, device);
|
||||||
if (client.registerDeathRecipient()) {
|
if (client.registerDeathRecipient()) {
|
||||||
mSpeakerphoneClients.add(0, client);
|
mCommunicationRouteClients.add(0, client);
|
||||||
return true;
|
return client;
|
||||||
}
|
}
|
||||||
return false;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@GuardedBy("mDeviceStateLock")
|
@GuardedBy("mDeviceStateLock")
|
||||||
private SpeakerphoneClient getSpeakerphoneClientForPid(int pid) {
|
private CommunicationRouteClient getCommunicationRouteClientForPid(int pid) {
|
||||||
for (SpeakerphoneClient cl : mSpeakerphoneClients) {
|
for (CommunicationRouteClient cl : mCommunicationRouteClients) {
|
||||||
if (cl.getPid() == pid) {
|
if (cl.getPid() == pid) {
|
||||||
return cl;
|
return cl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// List of clients requesting speakerPhone ON
|
|
||||||
@GuardedBy("mDeviceStateLock")
|
|
||||||
private final @NonNull ArrayList<SpeakerphoneClient> mSpeakerphoneClients =
|
|
||||||
new ArrayList<SpeakerphoneClient>();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -648,6 +648,10 @@ public class AudioDeviceInventory {
|
|||||||
/*package*/ int setPreferredDevicesForStrategySync(int strategy,
|
/*package*/ int setPreferredDevicesForStrategySync(int strategy,
|
||||||
@NonNull List<AudioDeviceAttributes> devices) {
|
@NonNull List<AudioDeviceAttributes> devices) {
|
||||||
final long identity = Binder.clearCallingIdentity();
|
final long identity = Binder.clearCallingIdentity();
|
||||||
|
|
||||||
|
AudioService.sDeviceLogger.log((new AudioEventLogger.StringEvent(
|
||||||
|
"setPreferredDevicesForStrategySync, strategy: " + strategy
|
||||||
|
+ " devices: " + devices)).printLog(TAG));
|
||||||
final int status = mAudioSystem.setDevicesRoleForStrategy(
|
final int status = mAudioSystem.setDevicesRoleForStrategy(
|
||||||
strategy, AudioSystem.DEVICE_ROLE_PREFERRED, devices);
|
strategy, AudioSystem.DEVICE_ROLE_PREFERRED, devices);
|
||||||
Binder.restoreCallingIdentity(identity);
|
Binder.restoreCallingIdentity(identity);
|
||||||
|
|||||||
@@ -85,6 +85,7 @@ import android.media.IAudioRoutesObserver;
|
|||||||
import android.media.IAudioServerStateDispatcher;
|
import android.media.IAudioServerStateDispatcher;
|
||||||
import android.media.IAudioService;
|
import android.media.IAudioService;
|
||||||
import android.media.ICapturePresetDevicesRoleDispatcher;
|
import android.media.ICapturePresetDevicesRoleDispatcher;
|
||||||
|
import android.media.ICommunicationDeviceDispatcher;
|
||||||
import android.media.IPlaybackConfigDispatcher;
|
import android.media.IPlaybackConfigDispatcher;
|
||||||
import android.media.IRecordingConfigDispatcher;
|
import android.media.IRecordingConfigDispatcher;
|
||||||
import android.media.IRingtonePlayer;
|
import android.media.IRingtonePlayer;
|
||||||
@@ -207,6 +208,9 @@ public class AudioService extends IAudioService.Stub
|
|||||||
/** debug calls to devices APIs */
|
/** debug calls to devices APIs */
|
||||||
protected static final boolean DEBUG_DEVICES = false;
|
protected static final boolean DEBUG_DEVICES = false;
|
||||||
|
|
||||||
|
/** Debug communication route */
|
||||||
|
protected static final boolean DEBUG_COMM_RTE = false;
|
||||||
|
|
||||||
/** How long to delay before persisting a change in volume/ringer mode. */
|
/** How long to delay before persisting a change in volume/ringer mode. */
|
||||||
private static final int PERSIST_DELAY = 500;
|
private static final int PERSIST_DELAY = 500;
|
||||||
|
|
||||||
@@ -3689,7 +3693,7 @@ public class AudioService extends IAudioService.Stub
|
|||||||
final boolean ringerModeMute = ringerMode == AudioManager.RINGER_MODE_VIBRATE
|
final boolean ringerModeMute = ringerMode == AudioManager.RINGER_MODE_VIBRATE
|
||||||
|| ringerMode == AudioManager.RINGER_MODE_SILENT;
|
|| ringerMode == AudioManager.RINGER_MODE_SILENT;
|
||||||
final boolean shouldRingSco = ringerMode == AudioManager.RINGER_MODE_VIBRATE
|
final boolean shouldRingSco = ringerMode == AudioManager.RINGER_MODE_VIBRATE
|
||||||
&& isBluetoothScoOn();
|
&& mDeviceBroker.isBluetoothScoOn();
|
||||||
// Ask audio policy engine to force use Bluetooth SCO channel if needed
|
// Ask audio policy engine to force use Bluetooth SCO channel if needed
|
||||||
final String eventSource = "muteRingerModeStreams() from u/pid:" + Binder.getCallingUid()
|
final String eventSource = "muteRingerModeStreams() from u/pid:" + Binder.getCallingUid()
|
||||||
+ "/" + Binder.getCallingPid();
|
+ "/" + Binder.getCallingPid();
|
||||||
@@ -4265,6 +4269,115 @@ public class AudioService extends IAudioService.Stub
|
|||||||
restoreDeviceVolumeBehavior();
|
restoreDeviceVolumeBehavior();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final int[] VALID_COMMUNICATION_DEVICE_TYPES = {
|
||||||
|
AudioDeviceInfo.TYPE_BUILTIN_SPEAKER,
|
||||||
|
AudioDeviceInfo.TYPE_BLUETOOTH_SCO,
|
||||||
|
AudioDeviceInfo.TYPE_WIRED_HEADSET,
|
||||||
|
AudioDeviceInfo.TYPE_USB_HEADSET,
|
||||||
|
AudioDeviceInfo.TYPE_BUILTIN_EARPIECE,
|
||||||
|
AudioDeviceInfo.TYPE_WIRED_HEADPHONES,
|
||||||
|
AudioDeviceInfo.TYPE_HEARING_AID,
|
||||||
|
AudioDeviceInfo.TYPE_BLE_HEADSET,
|
||||||
|
AudioDeviceInfo.TYPE_USB_DEVICE,
|
||||||
|
AudioDeviceInfo.TYPE_BLE_SPEAKER,
|
||||||
|
AudioDeviceInfo.TYPE_LINE_ANALOG,
|
||||||
|
AudioDeviceInfo.TYPE_HDMI,
|
||||||
|
AudioDeviceInfo.TYPE_AUX_LINE
|
||||||
|
};
|
||||||
|
|
||||||
|
private boolean isValidCommunicationDevice(AudioDeviceInfo device) {
|
||||||
|
for (int type : VALID_COMMUNICATION_DEVICE_TYPES) {
|
||||||
|
if (device.getType() == type) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @see AudioManager#setDeviceForCommunication(int) */
|
||||||
|
public boolean setDeviceForCommunication(IBinder cb, int portId) {
|
||||||
|
final int uid = Binder.getCallingUid();
|
||||||
|
final int pid = Binder.getCallingPid();
|
||||||
|
|
||||||
|
AudioDeviceInfo device = null;
|
||||||
|
if (portId != 0) {
|
||||||
|
device = AudioManager.getDeviceForPortId(portId, AudioManager.GET_DEVICES_OUTPUTS);
|
||||||
|
if (device == null) {
|
||||||
|
throw new IllegalArgumentException("invalid portID " + portId);
|
||||||
|
}
|
||||||
|
if (!isValidCommunicationDevice(device)) {
|
||||||
|
throw new IllegalArgumentException("invalid device type " + device.getType());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final String eventSource = new StringBuilder("setDeviceForCommunication(")
|
||||||
|
.append(") from u/pid:").append(uid).append("/")
|
||||||
|
.append(pid).toString();
|
||||||
|
|
||||||
|
int deviceType = AudioSystem.DEVICE_OUT_DEFAULT;
|
||||||
|
String deviceAddress = null;
|
||||||
|
if (device != null) {
|
||||||
|
deviceType = device.getPort().type();
|
||||||
|
deviceAddress = device.getAddress();
|
||||||
|
} else {
|
||||||
|
AudioDeviceInfo curDevice = mDeviceBroker.getDeviceForCommunication();
|
||||||
|
if (curDevice != null) {
|
||||||
|
deviceType = curDevice.getPort().type();
|
||||||
|
deviceAddress = curDevice.getAddress();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// do not log metrics if clearing communication device while no communication device
|
||||||
|
// was selected
|
||||||
|
if (deviceType != AudioSystem.DEVICE_OUT_DEFAULT) {
|
||||||
|
new MediaMetrics.Item(MediaMetrics.Name.AUDIO_DEVICE
|
||||||
|
+ MediaMetrics.SEPARATOR + "setDeviceForCommunication")
|
||||||
|
.set(MediaMetrics.Property.DEVICE,
|
||||||
|
AudioSystem.getDeviceName(deviceType))
|
||||||
|
.set(MediaMetrics.Property.ADDRESS, deviceAddress)
|
||||||
|
.set(MediaMetrics.Property.STATE, device != null
|
||||||
|
? MediaMetrics.Value.CONNECTED : MediaMetrics.Value.DISCONNECTED)
|
||||||
|
.record();
|
||||||
|
}
|
||||||
|
|
||||||
|
final long ident = Binder.clearCallingIdentity();
|
||||||
|
boolean status =
|
||||||
|
mDeviceBroker.setDeviceForCommunication(cb, pid, device, eventSource);
|
||||||
|
Binder.restoreCallingIdentity(ident);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @see AudioManager#getDeviceForCommunication() */
|
||||||
|
public int getDeviceForCommunication() {
|
||||||
|
final long ident = Binder.clearCallingIdentity();
|
||||||
|
AudioDeviceInfo device = mDeviceBroker.getDeviceForCommunication();
|
||||||
|
Binder.restoreCallingIdentity(ident);
|
||||||
|
if (device == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return device.getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @see AudioManager#addOnCommunicationDeviceChangedListener(
|
||||||
|
* Executor, AudioManager.OnCommunicationDeviceChangedListener)
|
||||||
|
*/
|
||||||
|
public void registerCommunicationDeviceDispatcher(
|
||||||
|
@Nullable ICommunicationDeviceDispatcher dispatcher) {
|
||||||
|
if (dispatcher == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mDeviceBroker.registerCommunicationDeviceDispatcher(dispatcher);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @see AudioManager#removeOnCommunicationDeviceChangedListener(
|
||||||
|
* AudioManager.OnCommunicationDeviceChangedListener)
|
||||||
|
*/
|
||||||
|
public void unregisterCommunicationDeviceDispatcher(
|
||||||
|
@Nullable ICommunicationDeviceDispatcher dispatcher) {
|
||||||
|
if (dispatcher == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mDeviceBroker.unregisterCommunicationDeviceDispatcher(dispatcher);
|
||||||
|
}
|
||||||
|
|
||||||
/** @see AudioManager#setSpeakerphoneOn(boolean) */
|
/** @see AudioManager#setSpeakerphoneOn(boolean) */
|
||||||
public void setSpeakerphoneOn(IBinder cb, boolean on) {
|
public void setSpeakerphoneOn(IBinder cb, boolean on) {
|
||||||
if (!checkAudioSettingsPermission("setSpeakerphoneOn()")) {
|
if (!checkAudioSettingsPermission("setSpeakerphoneOn()")) {
|
||||||
@@ -4274,10 +4387,10 @@ public class AudioService extends IAudioService.Stub
|
|||||||
// for logging only
|
// for logging only
|
||||||
final int uid = Binder.getCallingUid();
|
final int uid = Binder.getCallingUid();
|
||||||
final int pid = Binder.getCallingPid();
|
final int pid = Binder.getCallingPid();
|
||||||
|
|
||||||
final String eventSource = new StringBuilder("setSpeakerphoneOn(").append(on)
|
final String eventSource = new StringBuilder("setSpeakerphoneOn(").append(on)
|
||||||
.append(") from u/pid:").append(uid).append("/")
|
.append(") from u/pid:").append(uid).append("/")
|
||||||
.append(pid).toString();
|
.append(pid).toString();
|
||||||
final boolean stateChanged = mDeviceBroker.setSpeakerphoneOn(cb, pid, on, eventSource);
|
|
||||||
new MediaMetrics.Item(MediaMetrics.Name.AUDIO_DEVICE
|
new MediaMetrics.Item(MediaMetrics.Name.AUDIO_DEVICE
|
||||||
+ MediaMetrics.SEPARATOR + "setSpeakerphoneOn")
|
+ MediaMetrics.SEPARATOR + "setSpeakerphoneOn")
|
||||||
.setUid(uid)
|
.setUid(uid)
|
||||||
@@ -4285,17 +4398,9 @@ public class AudioService extends IAudioService.Stub
|
|||||||
.set(MediaMetrics.Property.STATE, on
|
.set(MediaMetrics.Property.STATE, on
|
||||||
? MediaMetrics.Value.ON : MediaMetrics.Value.OFF)
|
? MediaMetrics.Value.ON : MediaMetrics.Value.OFF)
|
||||||
.record();
|
.record();
|
||||||
|
final long ident = Binder.clearCallingIdentity();
|
||||||
if (stateChanged) {
|
mDeviceBroker.setSpeakerphoneOn(cb, pid, on, eventSource);
|
||||||
final long ident = Binder.clearCallingIdentity();
|
Binder.restoreCallingIdentity(ident);
|
||||||
try {
|
|
||||||
mContext.sendBroadcastAsUser(
|
|
||||||
new Intent(AudioManager.ACTION_SPEAKERPHONE_STATE_CHANGED)
|
|
||||||
.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), UserHandle.ALL);
|
|
||||||
} finally {
|
|
||||||
Binder.restoreCallingIdentity(ident);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @see AudioManager#isSpeakerphoneOn() */
|
/** @see AudioManager#isSpeakerphoneOn() */
|
||||||
@@ -4303,6 +4408,11 @@ public class AudioService extends IAudioService.Stub
|
|||||||
return mDeviceBroker.isSpeakerphoneOn();
|
return mDeviceBroker.isSpeakerphoneOn();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** BT SCO audio state seen by apps using the deprecated API setBluetoothScoOn().
|
||||||
|
* @see isBluetoothScoOn() */
|
||||||
|
private boolean mBtScoOnByApp;
|
||||||
|
|
||||||
/** @see AudioManager#setBluetoothScoOn(boolean) */
|
/** @see AudioManager#setBluetoothScoOn(boolean) */
|
||||||
public void setBluetoothScoOn(boolean on) {
|
public void setBluetoothScoOn(boolean on) {
|
||||||
if (!checkAudioSettingsPermission("setBluetoothScoOn()")) {
|
if (!checkAudioSettingsPermission("setBluetoothScoOn()")) {
|
||||||
@@ -4311,7 +4421,7 @@ public class AudioService extends IAudioService.Stub
|
|||||||
|
|
||||||
// Only enable calls from system components
|
// Only enable calls from system components
|
||||||
if (UserHandle.getCallingAppId() >= FIRST_APPLICATION_UID) {
|
if (UserHandle.getCallingAppId() >= FIRST_APPLICATION_UID) {
|
||||||
mDeviceBroker.setBluetoothScoOnByApp(on);
|
mBtScoOnByApp = on;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4337,7 +4447,7 @@ public class AudioService extends IAudioService.Stub
|
|||||||
* Note that it doesn't report internal state, but state seen by apps (which may have
|
* Note that it doesn't report internal state, but state seen by apps (which may have
|
||||||
* called setBluetoothScoOn() */
|
* called setBluetoothScoOn() */
|
||||||
public boolean isBluetoothScoOn() {
|
public boolean isBluetoothScoOn() {
|
||||||
return mDeviceBroker.isBluetoothScoOnForApp();
|
return mBtScoOnByApp || mDeviceBroker.isBluetoothScoOn();
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO investigate internal users due to deprecation of SDK API
|
// TODO investigate internal users due to deprecation of SDK API
|
||||||
@@ -4384,7 +4494,7 @@ public class AudioService extends IAudioService.Stub
|
|||||||
.set(MediaMetrics.Property.SCO_AUDIO_MODE,
|
.set(MediaMetrics.Property.SCO_AUDIO_MODE,
|
||||||
BtHelper.scoAudioModeToString(scoAudioMode))
|
BtHelper.scoAudioModeToString(scoAudioMode))
|
||||||
.record();
|
.record();
|
||||||
startBluetoothScoInt(cb, scoAudioMode, eventSource);
|
startBluetoothScoInt(cb, pid, scoAudioMode, eventSource);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4403,10 +4513,10 @@ public class AudioService extends IAudioService.Stub
|
|||||||
.set(MediaMetrics.Property.SCO_AUDIO_MODE,
|
.set(MediaMetrics.Property.SCO_AUDIO_MODE,
|
||||||
BtHelper.scoAudioModeToString(BtHelper.SCO_MODE_VIRTUAL_CALL))
|
BtHelper.scoAudioModeToString(BtHelper.SCO_MODE_VIRTUAL_CALL))
|
||||||
.record();
|
.record();
|
||||||
startBluetoothScoInt(cb, BtHelper.SCO_MODE_VIRTUAL_CALL, eventSource);
|
startBluetoothScoInt(cb, pid, BtHelper.SCO_MODE_VIRTUAL_CALL, eventSource);
|
||||||
}
|
}
|
||||||
|
|
||||||
void startBluetoothScoInt(IBinder cb, int scoAudioMode, @NonNull String eventSource) {
|
void startBluetoothScoInt(IBinder cb, int pid, int scoAudioMode, @NonNull String eventSource) {
|
||||||
MediaMetrics.Item mmi = new MediaMetrics.Item(MediaMetrics.Name.AUDIO_BLUETOOTH)
|
MediaMetrics.Item mmi = new MediaMetrics.Item(MediaMetrics.Name.AUDIO_BLUETOOTH)
|
||||||
.set(MediaMetrics.Property.EVENT, "startBluetoothScoInt")
|
.set(MediaMetrics.Property.EVENT, "startBluetoothScoInt")
|
||||||
.set(MediaMetrics.Property.SCO_AUDIO_MODE,
|
.set(MediaMetrics.Property.SCO_AUDIO_MODE,
|
||||||
@@ -4417,9 +4527,9 @@ public class AudioService extends IAudioService.Stub
|
|||||||
mmi.set(MediaMetrics.Property.EARLY_RETURN, "permission or systemReady").record();
|
mmi.set(MediaMetrics.Property.EARLY_RETURN, "permission or systemReady").record();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
synchronized (mDeviceBroker.mSetModeLock) {
|
final long ident = Binder.clearCallingIdentity();
|
||||||
mDeviceBroker.startBluetoothScoForClient_Sync(cb, scoAudioMode, eventSource);
|
mDeviceBroker.startBluetoothScoForClient(cb, pid, scoAudioMode, eventSource);
|
||||||
}
|
Binder.restoreCallingIdentity(ident);
|
||||||
mmi.record();
|
mmi.record();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4434,9 +4544,9 @@ public class AudioService extends IAudioService.Stub
|
|||||||
final String eventSource = new StringBuilder("stopBluetoothSco()")
|
final String eventSource = new StringBuilder("stopBluetoothSco()")
|
||||||
.append(") from u/pid:").append(uid).append("/")
|
.append(") from u/pid:").append(uid).append("/")
|
||||||
.append(pid).toString();
|
.append(pid).toString();
|
||||||
synchronized (mDeviceBroker.mSetModeLock) {
|
final long ident = Binder.clearCallingIdentity();
|
||||||
mDeviceBroker.stopBluetoothScoForClient_Sync(cb, eventSource);
|
mDeviceBroker.stopBluetoothScoForClient(cb, pid, eventSource);
|
||||||
}
|
Binder.restoreCallingIdentity(ident);
|
||||||
new MediaMetrics.Item(MediaMetrics.Name.AUDIO_BLUETOOTH)
|
new MediaMetrics.Item(MediaMetrics.Name.AUDIO_BLUETOOTH)
|
||||||
.setUid(uid)
|
.setUid(uid)
|
||||||
.setPid(pid)
|
.setPid(pid)
|
||||||
@@ -4862,8 +4972,7 @@ public class AudioService extends IAudioService.Stub
|
|||||||
switch (mPlatformType) {
|
switch (mPlatformType) {
|
||||||
case AudioSystem.PLATFORM_VOICE:
|
case AudioSystem.PLATFORM_VOICE:
|
||||||
if (isInCommunication()) {
|
if (isInCommunication()) {
|
||||||
if (AudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION)
|
if (mDeviceBroker.isBluetoothScoOn()) {
|
||||||
== AudioSystem.FORCE_BT_SCO) {
|
|
||||||
// Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO...");
|
// Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO...");
|
||||||
return AudioSystem.STREAM_BLUETOOTH_SCO;
|
return AudioSystem.STREAM_BLUETOOTH_SCO;
|
||||||
} else {
|
} else {
|
||||||
@@ -4899,8 +5008,7 @@ public class AudioService extends IAudioService.Stub
|
|||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
if (isInCommunication()) {
|
if (isInCommunication()) {
|
||||||
if (AudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION)
|
if (mDeviceBroker.isBluetoothScoOn()) {
|
||||||
== AudioSystem.FORCE_BT_SCO) {
|
|
||||||
if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO");
|
if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO");
|
||||||
return AudioSystem.STREAM_BLUETOOTH_SCO;
|
return AudioSystem.STREAM_BLUETOOTH_SCO;
|
||||||
} else {
|
} else {
|
||||||
@@ -7670,6 +7778,7 @@ public class AudioService extends IAudioService.Stub
|
|||||||
pw.print(" mHasVibrator="); pw.println(mHasVibrator);
|
pw.print(" mHasVibrator="); pw.println(mHasVibrator);
|
||||||
pw.print(" mVolumePolicy="); pw.println(mVolumePolicy);
|
pw.print(" mVolumePolicy="); pw.println(mVolumePolicy);
|
||||||
pw.print(" mAvrcpAbsVolSupported="); pw.println(mAvrcpAbsVolSupported);
|
pw.print(" mAvrcpAbsVolSupported="); pw.println(mAvrcpAbsVolSupported);
|
||||||
|
pw.print(" mBtScoOnByApp="); pw.println(mBtScoOnByApp);
|
||||||
pw.print(" mIsSingleVolume="); pw.println(mIsSingleVolume);
|
pw.print(" mIsSingleVolume="); pw.println(mIsSingleVolume);
|
||||||
pw.print(" mUseFixedVolume="); pw.println(mUseFixedVolume);
|
pw.print(" mUseFixedVolume="); pw.println(mUseFixedVolume);
|
||||||
pw.print(" mFixedVolumeDevices="); pw.println(dumpDeviceTypes(mFixedVolumeDevices));
|
pw.print(" mFixedVolumeDevices="); pw.println(dumpDeviceTypes(mFixedVolumeDevices));
|
||||||
|
|||||||
@@ -27,11 +27,10 @@ import android.bluetooth.BluetoothHeadset;
|
|||||||
import android.bluetooth.BluetoothHearingAid;
|
import android.bluetooth.BluetoothHearingAid;
|
||||||
import android.bluetooth.BluetoothProfile;
|
import android.bluetooth.BluetoothProfile;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.media.AudioDeviceAttributes;
|
||||||
import android.media.AudioManager;
|
import android.media.AudioManager;
|
||||||
import android.media.AudioSystem;
|
import android.media.AudioSystem;
|
||||||
import android.os.Binder;
|
import android.os.Binder;
|
||||||
import android.os.IBinder;
|
|
||||||
import android.os.RemoteException;
|
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
@@ -39,9 +38,7 @@ import android.util.Log;
|
|||||||
import com.android.internal.annotations.GuardedBy;
|
import com.android.internal.annotations.GuardedBy;
|
||||||
|
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.NoSuchElementException;
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -58,10 +55,6 @@ public class BtHelper {
|
|||||||
mDeviceBroker = broker;
|
mDeviceBroker = broker;
|
||||||
}
|
}
|
||||||
|
|
||||||
// List of clients having issued a SCO start request
|
|
||||||
@GuardedBy("BtHelper.this")
|
|
||||||
private final @NonNull ArrayList<ScoClient> mScoClients = new ArrayList<ScoClient>();
|
|
||||||
|
|
||||||
// BluetoothHeadset API to control SCO connection
|
// BluetoothHeadset API to control SCO connection
|
||||||
private @Nullable BluetoothHeadset mBluetoothHeadset;
|
private @Nullable BluetoothHeadset mBluetoothHeadset;
|
||||||
|
|
||||||
@@ -301,6 +294,8 @@ public class BtHelper {
|
|||||||
@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
|
@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
|
||||||
/*package*/ synchronized void receiveBtEvent(Intent intent) {
|
/*package*/ synchronized void receiveBtEvent(Intent intent) {
|
||||||
final String action = intent.getAction();
|
final String action = intent.getAction();
|
||||||
|
|
||||||
|
Log.i(TAG, "receiveBtEvent action: " + action + " mScoAudioState: " + mScoAudioState);
|
||||||
if (action.equals(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED)) {
|
if (action.equals(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED)) {
|
||||||
BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
|
BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
|
||||||
setBtScoActiveDevice(btDevice);
|
setBtScoActiveDevice(btDevice);
|
||||||
@@ -308,20 +303,16 @@ public class BtHelper {
|
|||||||
boolean broadcast = false;
|
boolean broadcast = false;
|
||||||
int scoAudioState = AudioManager.SCO_AUDIO_STATE_ERROR;
|
int scoAudioState = AudioManager.SCO_AUDIO_STATE_ERROR;
|
||||||
int btState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
|
int btState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
|
||||||
// broadcast intent if the connection was initated by AudioService
|
Log.i(TAG, "receiveBtEvent ACTION_AUDIO_STATE_CHANGED: " + btState);
|
||||||
if (!mScoClients.isEmpty()
|
|
||||||
&& (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL
|
|
||||||
|| mScoAudioState == SCO_STATE_ACTIVATE_REQ
|
|
||||||
|| mScoAudioState == SCO_STATE_DEACTIVATE_REQ
|
|
||||||
|| mScoAudioState == SCO_STATE_DEACTIVATING)) {
|
|
||||||
broadcast = true;
|
|
||||||
}
|
|
||||||
switch (btState) {
|
switch (btState) {
|
||||||
case BluetoothHeadset.STATE_AUDIO_CONNECTED:
|
case BluetoothHeadset.STATE_AUDIO_CONNECTED:
|
||||||
scoAudioState = AudioManager.SCO_AUDIO_STATE_CONNECTED;
|
scoAudioState = AudioManager.SCO_AUDIO_STATE_CONNECTED;
|
||||||
if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL
|
if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL
|
||||||
&& mScoAudioState != SCO_STATE_DEACTIVATE_REQ) {
|
&& mScoAudioState != SCO_STATE_DEACTIVATE_REQ) {
|
||||||
mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
|
mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
|
||||||
|
} else if (mDeviceBroker.isBluetoothScoRequested()) {
|
||||||
|
// broadcast intent if the connection was initated by AudioService
|
||||||
|
broadcast = true;
|
||||||
}
|
}
|
||||||
mDeviceBroker.setBluetoothScoOn(true, "BtHelper.receiveBtEvent");
|
mDeviceBroker.setBluetoothScoOn(true, "BtHelper.receiveBtEvent");
|
||||||
break;
|
break;
|
||||||
@@ -333,21 +324,21 @@ public class BtHelper {
|
|||||||
// notified by requestScoState() setting state to SCO_STATE_ACTIVATE_REQ.
|
// notified by requestScoState() setting state to SCO_STATE_ACTIVATE_REQ.
|
||||||
// 2) If audio was connected then disconnected via Bluetooth APIs and
|
// 2) If audio was connected then disconnected via Bluetooth APIs and
|
||||||
// we still have pending activation requests by apps: this is indicated by
|
// we still have pending activation requests by apps: this is indicated by
|
||||||
// state SCO_STATE_ACTIVE_EXTERNAL and the mScoClients list not empty.
|
// state SCO_STATE_ACTIVE_EXTERNAL and BT SCO is requested.
|
||||||
if (mScoAudioState == SCO_STATE_ACTIVATE_REQ
|
if (mScoAudioState == SCO_STATE_ACTIVATE_REQ
|
||||||
|| (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL
|
|| (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL
|
||||||
&& !mScoClients.isEmpty())) {
|
&& mDeviceBroker.isBluetoothScoRequested())) {
|
||||||
if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null
|
if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null
|
||||||
&& connectBluetoothScoAudioHelper(mBluetoothHeadset,
|
&& connectBluetoothScoAudioHelper(mBluetoothHeadset,
|
||||||
mBluetoothHeadsetDevice, mScoAudioMode)) {
|
mBluetoothHeadsetDevice, mScoAudioMode)) {
|
||||||
mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
|
mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
|
||||||
broadcast = false;
|
scoAudioState = AudioManager.SCO_AUDIO_STATE_CONNECTING;
|
||||||
|
broadcast = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Tear down SCO if disconnected from external
|
if (mScoAudioState != SCO_STATE_ACTIVE_EXTERNAL) {
|
||||||
if (mScoAudioState == SCO_STATE_DEACTIVATING) {
|
broadcast = true;
|
||||||
clearAllScoClients(0, false);
|
|
||||||
}
|
}
|
||||||
mScoAudioState = SCO_STATE_INACTIVE;
|
mScoAudioState = SCO_STATE_INACTIVE;
|
||||||
break;
|
break;
|
||||||
@@ -356,11 +347,8 @@ public class BtHelper {
|
|||||||
&& mScoAudioState != SCO_STATE_DEACTIVATE_REQ) {
|
&& mScoAudioState != SCO_STATE_DEACTIVATE_REQ) {
|
||||||
mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
|
mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
|
||||||
}
|
}
|
||||||
broadcast = false;
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
// do not broadcast CONNECTING or invalid state
|
|
||||||
broadcast = false;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (broadcast) {
|
if (broadcast) {
|
||||||
@@ -386,81 +374,19 @@ public class BtHelper {
|
|||||||
== BluetoothHeadset.STATE_AUDIO_CONNECTED;
|
== BluetoothHeadset.STATE_AUDIO_CONNECTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Disconnect all SCO connections started by {@link AudioManager} except those started by
|
|
||||||
* {@param exceptPid}
|
|
||||||
*
|
|
||||||
* @param exceptPid pid whose SCO connections through {@link AudioManager} should be kept
|
|
||||||
*/
|
|
||||||
// @GuardedBy("AudioDeviceBroker.mSetModeLock")
|
// @GuardedBy("AudioDeviceBroker.mSetModeLock")
|
||||||
@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
|
@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
|
||||||
/*package*/ synchronized void disconnectBluetoothSco(int exceptPid) {
|
/*package*/ synchronized boolean startBluetoothSco(int scoAudioMode,
|
||||||
checkScoAudioState();
|
|
||||||
if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
clearAllScoClients(exceptPid, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// @GuardedBy("AudioDeviceBroker.mSetModeLock")
|
|
||||||
@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
|
|
||||||
/*package*/ synchronized void startBluetoothScoForClient(IBinder cb, int scoAudioMode,
|
|
||||||
@NonNull String eventSource) {
|
@NonNull String eventSource) {
|
||||||
ScoClient client = getScoClient(cb, true);
|
|
||||||
// The calling identity must be cleared before calling ScoClient.incCount().
|
|
||||||
// inCount() calls requestScoState() which in turn can call BluetoothHeadset APIs
|
|
||||||
// and this must be done on behalf of system server to make sure permissions are granted.
|
|
||||||
// The caller identity must be cleared after getScoClient() because it is needed if a new
|
|
||||||
// client is created.
|
|
||||||
final long ident = Binder.clearCallingIdentity();
|
|
||||||
try {
|
|
||||||
AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(eventSource));
|
|
||||||
client.requestScoState(BluetoothHeadset.STATE_AUDIO_CONNECTED, scoAudioMode);
|
|
||||||
} catch (NullPointerException e) {
|
|
||||||
Log.e(TAG, "Null ScoClient", e);
|
|
||||||
}
|
|
||||||
Binder.restoreCallingIdentity(ident);
|
|
||||||
}
|
|
||||||
|
|
||||||
// @GuardedBy("AudioDeviceBroker.mSetModeLock")
|
|
||||||
@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
|
|
||||||
/*package*/ synchronized void stopBluetoothScoForClient(IBinder cb,
|
|
||||||
@NonNull String eventSource) {
|
|
||||||
ScoClient client = getScoClient(cb, false);
|
|
||||||
// The calling identity must be cleared before calling ScoClient.decCount().
|
|
||||||
// decCount() calls requestScoState() which in turn can call BluetoothHeadset APIs
|
|
||||||
// and this must be done on behalf of system server to make sure permissions are granted.
|
|
||||||
final long ident = Binder.clearCallingIdentity();
|
|
||||||
if (client != null) {
|
|
||||||
stopAndRemoveClient(client, eventSource);
|
|
||||||
}
|
|
||||||
Binder.restoreCallingIdentity(ident);
|
|
||||||
}
|
|
||||||
|
|
||||||
// @GuardedBy("AudioDeviceBroker.mSetModeLock")
|
|
||||||
@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
|
|
||||||
/*package*/ synchronized void stopBluetoothScoForPid(int pid) {
|
|
||||||
ScoClient client = getScoClientForPid(pid);
|
|
||||||
if (client == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final String eventSource = new StringBuilder("stopBluetoothScoForPid(")
|
|
||||||
.append(pid).append(")").toString();
|
|
||||||
stopAndRemoveClient(client, eventSource);
|
|
||||||
}
|
|
||||||
|
|
||||||
@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
|
|
||||||
// @GuardedBy("BtHelper.this")
|
|
||||||
private void stopAndRemoveClient(ScoClient client, @NonNull String eventSource) {
|
|
||||||
AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(eventSource));
|
AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(eventSource));
|
||||||
client.requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED,
|
return requestScoState(BluetoothHeadset.STATE_AUDIO_CONNECTED, scoAudioMode);
|
||||||
SCO_MODE_VIRTUAL_CALL);
|
}
|
||||||
// If a disconnection is pending, the client will be removed when clearAllScoClients()
|
|
||||||
// is called form receiveBtEvent()
|
// @GuardedBy("AudioDeviceBroker.mSetModeLock")
|
||||||
if (mScoAudioState != SCO_STATE_DEACTIVATE_REQ
|
@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
|
||||||
&& mScoAudioState != SCO_STATE_DEACTIVATING) {
|
/*package*/ synchronized boolean stopBluetoothSco(@NonNull String eventSource) {
|
||||||
client.remove(false /*stop */, true /*unregister*/);
|
AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(eventSource));
|
||||||
}
|
return requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED, SCO_MODE_VIRTUAL_CALL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*package*/ synchronized void setHearingAidVolume(int index, int streamType) {
|
/*package*/ synchronized void setHearingAidVolume(int index, int streamType) {
|
||||||
@@ -507,7 +433,6 @@ public class BtHelper {
|
|||||||
// @GuardedBy("AudioDeviceBroker.mSetModeLock")
|
// @GuardedBy("AudioDeviceBroker.mSetModeLock")
|
||||||
@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
|
@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
|
||||||
/*package*/ synchronized void resetBluetoothSco() {
|
/*package*/ synchronized void resetBluetoothSco() {
|
||||||
clearAllScoClients(0, false);
|
|
||||||
mScoAudioState = SCO_STATE_INACTIVE;
|
mScoAudioState = SCO_STATE_INACTIVE;
|
||||||
broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
|
broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
|
||||||
AudioSystem.setParameters("A2dpSuspended=false");
|
AudioSystem.setParameters("A2dpSuspended=false");
|
||||||
@@ -606,46 +531,64 @@ public class BtHelper {
|
|||||||
mDeviceBroker.postBroadcastScoConnectionState(state);
|
mDeviceBroker.postBroadcastScoConnectionState(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean handleBtScoActiveDeviceChange(BluetoothDevice btDevice, boolean isActive) {
|
@Nullable AudioDeviceAttributes getHeadsetAudioDevice() {
|
||||||
if (btDevice == null) {
|
if (mBluetoothHeadsetDevice == null) {
|
||||||
return true;
|
return null;
|
||||||
}
|
}
|
||||||
|
return btHeadsetDeviceToAudioDevice(mBluetoothHeadsetDevice);
|
||||||
|
}
|
||||||
|
|
||||||
|
private AudioDeviceAttributes btHeadsetDeviceToAudioDevice(BluetoothDevice btDevice) {
|
||||||
String address = btDevice.getAddress();
|
String address = btDevice.getAddress();
|
||||||
|
if (!BluetoothAdapter.checkBluetoothAddress(address)) {
|
||||||
|
address = "";
|
||||||
|
}
|
||||||
BluetoothClass btClass = btDevice.getBluetoothClass();
|
BluetoothClass btClass = btDevice.getBluetoothClass();
|
||||||
int inDevice = AudioSystem.DEVICE_IN_BLUETOOTH_SCO_HEADSET;
|
int nativeType = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO;
|
||||||
int[] outDeviceTypes = {
|
|
||||||
AudioSystem.DEVICE_OUT_BLUETOOTH_SCO,
|
|
||||||
AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET,
|
|
||||||
AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT
|
|
||||||
};
|
|
||||||
if (btClass != null) {
|
if (btClass != null) {
|
||||||
switch (btClass.getDeviceClass()) {
|
switch (btClass.getDeviceClass()) {
|
||||||
case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET:
|
case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET:
|
||||||
case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE:
|
case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE:
|
||||||
outDeviceTypes = new int[] { AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET };
|
nativeType = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET;
|
||||||
break;
|
break;
|
||||||
case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO:
|
case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO:
|
||||||
outDeviceTypes = new int[] { AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT };
|
nativeType = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!BluetoothAdapter.checkBluetoothAddress(address)) {
|
if (AudioService.DEBUG_DEVICES) {
|
||||||
address = "";
|
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);
|
String btDeviceName = getName(btDevice);
|
||||||
boolean result = false;
|
boolean result = false;
|
||||||
if (isActive) {
|
if (isActive) {
|
||||||
result |= mDeviceBroker.handleDeviceConnection(
|
result |= mDeviceBroker.handleDeviceConnection(isActive, audioDevice.getInternalType(),
|
||||||
isActive, outDeviceTypes[0], address, btDeviceName);
|
audioDevice.getAddress(), btDeviceName);
|
||||||
} else {
|
} else {
|
||||||
|
int[] outDeviceTypes = {
|
||||||
|
AudioSystem.DEVICE_OUT_BLUETOOTH_SCO,
|
||||||
|
AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET,
|
||||||
|
AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT
|
||||||
|
};
|
||||||
for (int outDeviceType : outDeviceTypes) {
|
for (int outDeviceType : outDeviceTypes) {
|
||||||
result |= mDeviceBroker.handleDeviceConnection(
|
result |= mDeviceBroker.handleDeviceConnection(
|
||||||
isActive, outDeviceType, address, btDeviceName);
|
isActive, outDeviceType, audioDevice.getAddress(), btDeviceName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// handleDeviceConnection() && result to make sure the method get executed
|
// handleDeviceConnection() && result to make sure the method get executed
|
||||||
result = mDeviceBroker.handleDeviceConnection(
|
result = mDeviceBroker.handleDeviceConnection(
|
||||||
isActive, inDevice, address, btDeviceName) && result;
|
isActive, inDevice, audioDevice.getAddress(), btDeviceName) && result;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -733,195 +676,122 @@ public class BtHelper {
|
|||||||
};
|
};
|
||||||
|
|
||||||
//----------------------------------------------------------------------
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
// @GuardedBy("AudioDeviceBroker.mSetModeLock")
|
// @GuardedBy("AudioDeviceBroker.mSetModeLock")
|
||||||
@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
|
//@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
|
||||||
/*package*/ synchronized void scoClientDied(Object obj) {
|
@GuardedBy("BtHelper.this")
|
||||||
final ScoClient client = (ScoClient) obj;
|
private boolean requestScoState(int state, int scoAudioMode) {
|
||||||
client.remove(true /*stop*/, false /*unregister*/);
|
checkScoAudioState();
|
||||||
Log.w(TAG, "SCO client died");
|
if (state == BluetoothHeadset.STATE_AUDIO_CONNECTED) {
|
||||||
}
|
// Make sure that the state transitions to CONNECTING even if we cannot initiate
|
||||||
|
// the connection.
|
||||||
private class ScoClient implements IBinder.DeathRecipient {
|
broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTING);
|
||||||
private IBinder mCb; // To be notified of client's death
|
switch (mScoAudioState) {
|
||||||
private int mCreatorPid;
|
case SCO_STATE_INACTIVE:
|
||||||
|
mScoAudioMode = scoAudioMode;
|
||||||
ScoClient(IBinder cb) {
|
if (scoAudioMode == SCO_MODE_UNDEFINED) {
|
||||||
mCb = cb;
|
mScoAudioMode = SCO_MODE_VIRTUAL_CALL;
|
||||||
mCreatorPid = Binder.getCallingPid();
|
if (mBluetoothHeadsetDevice != null) {
|
||||||
}
|
mScoAudioMode = Settings.Global.getInt(
|
||||||
|
mDeviceBroker.getContentResolver(),
|
||||||
public void registerDeathRecipient() {
|
"bluetooth_sco_channel_"
|
||||||
try {
|
+ mBluetoothHeadsetDevice.getAddress(),
|
||||||
mCb.linkToDeath(this, 0);
|
SCO_MODE_VIRTUAL_CALL);
|
||||||
} catch (RemoteException e) {
|
if (mScoAudioMode > SCO_MODE_MAX || mScoAudioMode < 0) {
|
||||||
Log.w(TAG, "ScoClient could not link to " + mCb + " binder death");
|
mScoAudioMode = SCO_MODE_VIRTUAL_CALL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
public void unregisterDeathRecipient() {
|
if (mBluetoothHeadset == null) {
|
||||||
try {
|
if (getBluetoothHeadset()) {
|
||||||
mCb.unlinkToDeath(this, 0);
|
mScoAudioState = SCO_STATE_ACTIVATE_REQ;
|
||||||
} catch (NoSuchElementException e) {
|
} else {
|
||||||
Log.w(TAG, "ScoClient could not not unregistered to binder");
|
Log.w(TAG, "requestScoState: getBluetoothHeadset failed during"
|
||||||
}
|
+ " connection, mScoAudioMode=" + mScoAudioMode);
|
||||||
}
|
broadcastScoConnectionState(
|
||||||
|
AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
|
||||||
@Override
|
return false;
|
||||||
public void binderDied() {
|
}
|
||||||
// process this from DeviceBroker's message queue to take the right locks since
|
break;
|
||||||
// this event can impact SCO mode and requires querying audio mode stack
|
}
|
||||||
mDeviceBroker.postScoClientDied(this);
|
if (mBluetoothHeadsetDevice == null) {
|
||||||
}
|
Log.w(TAG, "requestScoState: no active device while connecting,"
|
||||||
|
+ " mScoAudioMode=" + mScoAudioMode);
|
||||||
IBinder getBinder() {
|
broadcastScoConnectionState(
|
||||||
return mCb;
|
AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
|
||||||
}
|
return false;
|
||||||
|
}
|
||||||
int getPid() {
|
if (connectBluetoothScoAudioHelper(mBluetoothHeadset,
|
||||||
return mCreatorPid;
|
mBluetoothHeadsetDevice, mScoAudioMode)) {
|
||||||
}
|
mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
|
||||||
|
} else {
|
||||||
// @GuardedBy("AudioDeviceBroker.mSetModeLock")
|
Log.w(TAG, "requestScoState: connect to "
|
||||||
//@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
|
+ mBluetoothHeadsetDevice
|
||||||
@GuardedBy("BtHelper.this")
|
+ " failed, mScoAudioMode=" + mScoAudioMode);
|
||||||
private boolean requestScoState(int state, int scoAudioMode) {
|
broadcastScoConnectionState(
|
||||||
checkScoAudioState();
|
AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
|
||||||
if (mScoClients.size() != 1) {
|
return false;
|
||||||
Log.i(TAG, "requestScoState: state=" + state + ", scoAudioMode=" + scoAudioMode
|
}
|
||||||
+ ", num SCO clients=" + mScoClients.size());
|
break;
|
||||||
return true;
|
case SCO_STATE_DEACTIVATING:
|
||||||
}
|
mScoAudioState = SCO_STATE_ACTIVATE_REQ;
|
||||||
if (state == BluetoothHeadset.STATE_AUDIO_CONNECTED) {
|
break;
|
||||||
// Make sure that the state transitions to CONNECTING even if we cannot initiate
|
case SCO_STATE_DEACTIVATE_REQ:
|
||||||
// the connection.
|
mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
|
||||||
broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTING);
|
broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTED);
|
||||||
// Accept SCO audio activation only in NORMAL audio mode or if the mode is
|
break;
|
||||||
// currently controlled by the same client process.
|
case SCO_STATE_ACTIVE_INTERNAL:
|
||||||
final int modeOwnerPid = mDeviceBroker.getModeOwnerPid();
|
Log.w(TAG, "requestScoState: already in ACTIVE mode, simply return");
|
||||||
if (modeOwnerPid != 0 && (modeOwnerPid != mCreatorPid)) {
|
break;
|
||||||
Log.w(TAG, "requestScoState: audio mode is not NORMAL and modeOwnerPid "
|
default:
|
||||||
+ modeOwnerPid + " != creatorPid " + mCreatorPid);
|
Log.w(TAG, "requestScoState: failed to connect in state "
|
||||||
|
+ mScoAudioState + ", scoAudioMode=" + scoAudioMode);
|
||||||
broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
|
broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
switch (mScoAudioState) {
|
} else if (state == BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
|
||||||
case SCO_STATE_INACTIVE:
|
switch (mScoAudioState) {
|
||||||
mScoAudioMode = scoAudioMode;
|
case SCO_STATE_ACTIVE_INTERNAL:
|
||||||
if (scoAudioMode == SCO_MODE_UNDEFINED) {
|
if (mBluetoothHeadset == null) {
|
||||||
mScoAudioMode = SCO_MODE_VIRTUAL_CALL;
|
if (getBluetoothHeadset()) {
|
||||||
if (mBluetoothHeadsetDevice != null) {
|
mScoAudioState = SCO_STATE_DEACTIVATE_REQ;
|
||||||
mScoAudioMode = Settings.Global.getInt(
|
|
||||||
mDeviceBroker.getContentResolver(),
|
|
||||||
"bluetooth_sco_channel_"
|
|
||||||
+ mBluetoothHeadsetDevice.getAddress(),
|
|
||||||
SCO_MODE_VIRTUAL_CALL);
|
|
||||||
if (mScoAudioMode > SCO_MODE_MAX || mScoAudioMode < 0) {
|
|
||||||
mScoAudioMode = SCO_MODE_VIRTUAL_CALL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (mBluetoothHeadset == null) {
|
|
||||||
if (getBluetoothHeadset()) {
|
|
||||||
mScoAudioState = SCO_STATE_ACTIVATE_REQ;
|
|
||||||
} else {
|
|
||||||
Log.w(TAG, "requestScoState: getBluetoothHeadset failed during"
|
|
||||||
+ " connection, mScoAudioMode=" + mScoAudioMode);
|
|
||||||
broadcastScoConnectionState(
|
|
||||||
AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (mBluetoothHeadsetDevice == null) {
|
|
||||||
Log.w(TAG, "requestScoState: no active device while connecting,"
|
|
||||||
+ " mScoAudioMode=" + mScoAudioMode);
|
|
||||||
broadcastScoConnectionState(
|
|
||||||
AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (connectBluetoothScoAudioHelper(mBluetoothHeadset,
|
|
||||||
mBluetoothHeadsetDevice, mScoAudioMode)) {
|
|
||||||
mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
|
|
||||||
} else {
|
} else {
|
||||||
Log.w(TAG, "requestScoState: connect to " + mBluetoothHeadsetDevice
|
Log.w(TAG, "requestScoState: getBluetoothHeadset failed during"
|
||||||
+ " failed, mScoAudioMode=" + mScoAudioMode);
|
+ " disconnection, mScoAudioMode=" + mScoAudioMode);
|
||||||
|
mScoAudioState = SCO_STATE_INACTIVE;
|
||||||
broadcastScoConnectionState(
|
broadcastScoConnectionState(
|
||||||
AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
|
AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SCO_STATE_DEACTIVATING:
|
}
|
||||||
mScoAudioState = SCO_STATE_ACTIVATE_REQ;
|
if (mBluetoothHeadsetDevice == null) {
|
||||||
break;
|
|
||||||
case SCO_STATE_DEACTIVATE_REQ:
|
|
||||||
mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
|
|
||||||
broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTED);
|
|
||||||
break;
|
|
||||||
case SCO_STATE_ACTIVE_INTERNAL:
|
|
||||||
Log.w(TAG, "requestScoState: already in ACTIVE mode, simply return");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
Log.w(TAG, "requestScoState: failed to connect in state "
|
|
||||||
+ mScoAudioState + ", scoAudioMode=" + scoAudioMode);
|
|
||||||
broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else if (state == BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
|
|
||||||
switch (mScoAudioState) {
|
|
||||||
case SCO_STATE_ACTIVE_INTERNAL:
|
|
||||||
if (mBluetoothHeadset == null) {
|
|
||||||
if (getBluetoothHeadset()) {
|
|
||||||
mScoAudioState = SCO_STATE_DEACTIVATE_REQ;
|
|
||||||
} else {
|
|
||||||
Log.w(TAG, "requestScoState: getBluetoothHeadset failed during"
|
|
||||||
+ " disconnection, mScoAudioMode=" + mScoAudioMode);
|
|
||||||
mScoAudioState = SCO_STATE_INACTIVE;
|
|
||||||
broadcastScoConnectionState(
|
|
||||||
AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (mBluetoothHeadsetDevice == null) {
|
|
||||||
mScoAudioState = SCO_STATE_INACTIVE;
|
|
||||||
broadcastScoConnectionState(
|
|
||||||
AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (disconnectBluetoothScoAudioHelper(mBluetoothHeadset,
|
|
||||||
mBluetoothHeadsetDevice, mScoAudioMode)) {
|
|
||||||
mScoAudioState = SCO_STATE_DEACTIVATING;
|
|
||||||
} else {
|
|
||||||
mScoAudioState = SCO_STATE_INACTIVE;
|
|
||||||
broadcastScoConnectionState(
|
|
||||||
AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case SCO_STATE_ACTIVATE_REQ:
|
|
||||||
mScoAudioState = SCO_STATE_INACTIVE;
|
mScoAudioState = SCO_STATE_INACTIVE;
|
||||||
broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
|
broadcastScoConnectionState(
|
||||||
|
AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
|
||||||
break;
|
break;
|
||||||
default:
|
}
|
||||||
Log.w(TAG, "requestScoState: failed to disconnect in state "
|
if (disconnectBluetoothScoAudioHelper(mBluetoothHeadset,
|
||||||
+ mScoAudioState + ", scoAudioMode=" + scoAudioMode);
|
mBluetoothHeadsetDevice, mScoAudioMode)) {
|
||||||
broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
|
mScoAudioState = SCO_STATE_DEACTIVATING;
|
||||||
return false;
|
} else {
|
||||||
}
|
mScoAudioState = SCO_STATE_INACTIVE;
|
||||||
|
broadcastScoConnectionState(
|
||||||
|
AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SCO_STATE_ACTIVATE_REQ:
|
||||||
|
mScoAudioState = SCO_STATE_INACTIVE;
|
||||||
|
broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Log.w(TAG, "requestScoState: failed to disconnect in state "
|
||||||
|
+ mScoAudioState + ", scoAudioMode=" + scoAudioMode);
|
||||||
|
broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@GuardedBy("BtHelper.this")
|
|
||||||
void remove(boolean stop, boolean unregister) {
|
|
||||||
if (unregister) {
|
|
||||||
unregisterDeathRecipient();
|
|
||||||
}
|
|
||||||
if (stop) {
|
|
||||||
requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED,
|
|
||||||
SCO_MODE_VIRTUAL_CALL);
|
|
||||||
}
|
|
||||||
mScoClients.remove(this);
|
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------
|
//-----------------------------------------------------
|
||||||
@@ -974,49 +844,6 @@ public class BtHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@GuardedBy("BtHelper.this")
|
|
||||||
private ScoClient getScoClient(IBinder cb, boolean create) {
|
|
||||||
for (ScoClient existingClient : mScoClients) {
|
|
||||||
if (existingClient.getBinder() == cb) {
|
|
||||||
return existingClient;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (create) {
|
|
||||||
ScoClient newClient = new ScoClient(cb);
|
|
||||||
newClient.registerDeathRecipient();
|
|
||||||
mScoClients.add(newClient);
|
|
||||||
return newClient;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@GuardedBy("BtHelper.this")
|
|
||||||
private ScoClient getScoClientForPid(int pid) {
|
|
||||||
for (ScoClient cl : mScoClients) {
|
|
||||||
if (cl.getPid() == pid) {
|
|
||||||
return cl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// @GuardedBy("AudioDeviceBroker.mSetModeLock")
|
|
||||||
//@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
|
|
||||||
@GuardedBy("BtHelper.this")
|
|
||||||
private void clearAllScoClients(int exceptPid, boolean stopSco) {
|
|
||||||
final ArrayList<ScoClient> clients = new ArrayList<ScoClient>();
|
|
||||||
for (ScoClient cl : mScoClients) {
|
|
||||||
if (cl.getPid() != exceptPid) {
|
|
||||||
clients.add(cl);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (ScoClient cl : clients) {
|
|
||||||
cl.remove(stopSco, true /*unregister*/);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean getBluetoothHeadset() {
|
private boolean getBluetoothHeadset() {
|
||||||
boolean result = false;
|
boolean result = false;
|
||||||
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
|
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
|
||||||
@@ -1062,10 +889,6 @@ public class BtHelper {
|
|||||||
pw.println(prefix + "mBluetoothHeadsetDevice: " + mBluetoothHeadsetDevice);
|
pw.println(prefix + "mBluetoothHeadsetDevice: " + mBluetoothHeadsetDevice);
|
||||||
pw.println(prefix + "mScoAudioState: " + scoAudioStateToString(mScoAudioState));
|
pw.println(prefix + "mScoAudioState: " + scoAudioStateToString(mScoAudioState));
|
||||||
pw.println(prefix + "mScoAudioMode: " + scoAudioModeToString(mScoAudioMode));
|
pw.println(prefix + "mScoAudioMode: " + scoAudioModeToString(mScoAudioMode));
|
||||||
pw.println(prefix + "Sco clients:");
|
|
||||||
mScoClients.forEach((cl) -> {
|
|
||||||
pw.println(" " + prefix + "pid: " + cl.getPid() + " cb: " + cl.getBinder()); });
|
|
||||||
|
|
||||||
pw.println("\n" + prefix + "mHearingAid: " + mHearingAid);
|
pw.println("\n" + prefix + "mHearingAid: " + mHearingAid);
|
||||||
pw.println(prefix + "mA2dp: " + mA2dp);
|
pw.println(prefix + "mA2dp: " + mA2dp);
|
||||||
pw.println(prefix + "mAvrcpAbsVolSupported: " + mAvrcpAbsVolSupported);
|
pw.println(prefix + "mAvrcpAbsVolSupported: " + mAvrcpAbsVolSupported);
|
||||||
|
|||||||
Reference in New Issue
Block a user