AudioManager: Add communication device management APIs am: f39726f262
Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/1534432 MUST ONLY BE SUBMITTED BY AUTOMERGER Change-Id: I6df2d1f67e71f5550edf7c0c490c7d50782f339d
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
//--------------------
|
//--------------------
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -31,6 +31,7 @@ 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.os.Binder;
|
import android.os.Binder;
|
||||||
@@ -39,6 +40,7 @@ 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.os.UserHandle;
|
||||||
@@ -233,6 +235,38 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Select device for use for communication use cases.
|
||||||
|
* @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")
|
||||||
/*package*/ void setCommunicationRouteForClient(
|
/*package*/ void setCommunicationRouteForClient(
|
||||||
IBinder cb, int pid, AudioDeviceAttributes device,
|
IBinder cb, int pid, AudioDeviceAttributes device,
|
||||||
@@ -278,6 +312,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
} else {
|
} else {
|
||||||
removeCommunicationRouteClient(cb, true);
|
removeCommunicationRouteClient(cb, true);
|
||||||
}
|
}
|
||||||
|
postBroadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
|
||||||
}
|
}
|
||||||
} else if (!isBtScoRequested && wasBtScoRequested) {
|
} else if (!isBtScoRequested && wasBtScoRequested) {
|
||||||
mBtHelper.stopBluetoothSco(eventSource);
|
mBtHelper.stopBluetoothSco(eventSource);
|
||||||
@@ -324,6 +359,20 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
return device;
|
return device;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the device currently requested for communication use case.
|
||||||
|
* @return AudioDeviceInfo the requested device for communication.
|
||||||
|
*/
|
||||||
|
AudioDeviceInfo getDeviceForCommunication() {
|
||||||
|
synchronized (mDeviceStateLock) {
|
||||||
|
AudioDeviceAttributes device = requestedCommunicationDevice();
|
||||||
|
if (device == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return AudioManager.getDeviceInfoFromType(device.getType());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper method on top of requestedCommunicationDevice() indicating if
|
* Helper method on top of requestedCommunicationDevice() indicating if
|
||||||
* speakerphone ON is currently requested or not.
|
* speakerphone ON is currently requested or not.
|
||||||
@@ -566,11 +615,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
AudioDeviceAttributes device = new AudioDeviceAttributes(
|
AudioDeviceAttributes device = new AudioDeviceAttributes(
|
||||||
AudioDeviceAttributes.ROLE_OUTPUT, AudioDeviceInfo.TYPE_BLUETOOTH_SCO, "");
|
AudioDeviceAttributes.ROLE_OUTPUT, AudioDeviceInfo.TYPE_BLUETOOTH_SCO, "");
|
||||||
setCommunicationRouteForClient(cb, pid, device, scoAudioMode, eventSource);
|
setCommunicationRouteForClient(cb, pid, device, scoAudioMode, eventSource);
|
||||||
if (!isBluetoothScoRequested()) {
|
|
||||||
Log.w(TAG, "startBluetoothScoForClient_Sync: rejected for pid: "
|
|
||||||
+ pid + " mode owner pid: " + mModeOwnerPid);
|
|
||||||
postBroadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -632,6 +676,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
|
||||||
@@ -1571,6 +1654,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
mAudioService.postUpdateRingerModeServiceInt();
|
mAudioService.postUpdateRingerModeServiceInt();
|
||||||
|
dispatchCommunicationDevice();
|
||||||
}
|
}
|
||||||
|
|
||||||
private CommunicationRouteClient removeCommunicationRouteClient(
|
private CommunicationRouteClient removeCommunicationRouteClient(
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -4268,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()")) {
|
||||||
|
|||||||
@@ -707,7 +707,7 @@ public class BtHelper {
|
|||||||
mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
|
mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
|
||||||
} else {
|
} else {
|
||||||
Log.w(TAG, "requestScoState: connect to "
|
Log.w(TAG, "requestScoState: connect to "
|
||||||
+ getAnonymizedAddress(mBluetoothHeadsetDevice)
|
+ mBluetoothHeadsetDevice
|
||||||
+ " failed, mScoAudioMode=" + mScoAudioMode);
|
+ " failed, mScoAudioMode=" + mScoAudioMode);
|
||||||
broadcastScoConnectionState(
|
broadcastScoConnectionState(
|
||||||
AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
|
AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
|
||||||
|
|||||||
Reference in New Issue
Block a user