Merge "Revert "New APIs for immersive audio"" into sc-v2-dev

This commit is contained in:
Jean-Michel Trivi
2021-08-26 15:12:10 +00:00
committed by Android (Google) Code Review
9 changed files with 2 additions and 851 deletions

View File

@@ -20181,10 +20181,8 @@ package android.media {
method public int getAllowedCapturePolicy(); method public int getAllowedCapturePolicy();
method public int getContentType(); method public int getContentType();
method public int getFlags(); method public int getFlags();
method public int getSpatializationBehavior();
method public int getUsage(); method public int getUsage();
method public int getVolumeControlStream(); method public int getVolumeControlStream();
method public boolean isContentSpatialized();
method public void writeToParcel(android.os.Parcel, int); method public void writeToParcel(android.os.Parcel, int);
field public static final int ALLOW_CAPTURE_BY_ALL = 1; // 0x1 field public static final int ALLOW_CAPTURE_BY_ALL = 1; // 0x1
field public static final int ALLOW_CAPTURE_BY_NONE = 3; // 0x3 field public static final int ALLOW_CAPTURE_BY_NONE = 3; // 0x3
@@ -20198,8 +20196,6 @@ package android.media {
field public static final int FLAG_AUDIBILITY_ENFORCED = 1; // 0x1 field public static final int FLAG_AUDIBILITY_ENFORCED = 1; // 0x1
field public static final int FLAG_HW_AV_SYNC = 16; // 0x10 field public static final int FLAG_HW_AV_SYNC = 16; // 0x10
field @Deprecated public static final int FLAG_LOW_LATENCY = 256; // 0x100 field @Deprecated public static final int FLAG_LOW_LATENCY = 256; // 0x100
field public static final int SPATIALIZATION_BEHAVIOR_AUTO = 0; // 0x0
field public static final int SPATIALIZATION_BEHAVIOR_NEVER = 1; // 0x1
field public static final int USAGE_ALARM = 4; // 0x4 field public static final int USAGE_ALARM = 4; // 0x4
field public static final int USAGE_ASSISTANCE_ACCESSIBILITY = 11; // 0xb field public static final int USAGE_ASSISTANCE_ACCESSIBILITY = 11; // 0xb
field public static final int USAGE_ASSISTANCE_NAVIGATION_GUIDANCE = 12; // 0xc field public static final int USAGE_ASSISTANCE_NAVIGATION_GUIDANCE = 12; // 0xc
@@ -20226,9 +20222,7 @@ package android.media {
method public android.media.AudioAttributes.Builder setContentType(int); method public android.media.AudioAttributes.Builder setContentType(int);
method public android.media.AudioAttributes.Builder setFlags(int); method public android.media.AudioAttributes.Builder setFlags(int);
method @NonNull public android.media.AudioAttributes.Builder setHapticChannelsMuted(boolean); method @NonNull public android.media.AudioAttributes.Builder setHapticChannelsMuted(boolean);
method @NonNull public android.media.AudioAttributes.Builder setIsContentSpatialized(boolean);
method public android.media.AudioAttributes.Builder setLegacyStreamType(int); method public android.media.AudioAttributes.Builder setLegacyStreamType(int);
method @NonNull public android.media.AudioAttributes.Builder setSpatializationBehavior(int);
method public android.media.AudioAttributes.Builder setUsage(int); method public android.media.AudioAttributes.Builder setUsage(int);
} }
@@ -20453,7 +20447,6 @@ package android.media {
method public String getProperty(String); method public String getProperty(String);
method public int getRingerMode(); method public int getRingerMode();
method @Deprecated public int getRouting(int); method @Deprecated public int getRouting(int);
method @Nullable public android.media.Spatializer getSpatializer();
method public int getStreamMaxVolume(int); method public int getStreamMaxVolume(int);
method public int getStreamMinVolume(int); method public int getStreamMinVolume(int);
method public int getStreamVolume(int); method public int getStreamVolume(int);
@@ -23850,17 +23843,6 @@ package android.media {
method public void onLoadComplete(android.media.SoundPool, int, int); method public void onLoadComplete(android.media.SoundPool, int, int);
} }
public class Spatializer {
method public void addOnSpatializerEnabledChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.Spatializer.OnSpatializerEnabledChangedListener);
method public boolean canBeSpatialized(@NonNull android.media.AudioAttributes, @NonNull android.media.AudioFormat);
method public boolean isEnabled();
method public void removeOnSpatializerEnabledChangedListener(@NonNull android.media.Spatializer.OnSpatializerEnabledChangedListener);
}
public static interface Spatializer.OnSpatializerEnabledChangedListener {
method public void onSpatializerEnabledChanged(boolean);
}
public final class SubtitleData { public final class SubtitleData {
ctor public SubtitleData(int, long, long, @NonNull byte[]); ctor public SubtitleData(int, long, long, @NonNull byte[]);
method @NonNull public byte[] getData(); method @NonNull public byte[] getData();

View File

@@ -5372,12 +5372,6 @@ package android.media {
field public static final android.media.RouteDiscoveryPreference EMPTY; field public static final android.media.RouteDiscoveryPreference EMPTY;
} }
public class Spatializer {
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void addCompatibleAudioDevice(@NonNull android.media.AudioDeviceAttributes);
method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public java.util.List<android.media.AudioDeviceAttributes> getCompatibleAudioDevices();
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void removeCompatibleAudioDevice(@NonNull android.media.AudioDeviceAttributes);
}
} }
package android.media.audiofx { package android.media.audiofx {

View File

@@ -447,26 +447,13 @@ public final class AudioAttributes implements Parcelable {
*/ */
public static final int FLAG_CAPTURE_PRIVATE = 0x1 << 13; public static final int FLAG_CAPTURE_PRIVATE = 0x1 << 13;
/**
* @hide
* Flag indicating the audio content has been processed to provide a virtual multichannel
* audio experience
*/
public static final int FLAG_CONTENT_SPATIALIZED = 0x1 << 14;
/**
* @hide
* Flag indicating the audio content is to never be spatialized
*/
public static final int FLAG_NEVER_SPATIALIZE = 0x1 << 15;
// Note that even though FLAG_MUTE_HAPTIC is stored as a flag bit, it is not here since // Note that even though FLAG_MUTE_HAPTIC is stored as a flag bit, it is not here since
// it is known as a boolean value outside of AudioAttributes. // it is known as a boolean value outside of AudioAttributes.
private static final int FLAG_ALL = FLAG_AUDIBILITY_ENFORCED | FLAG_SECURE | FLAG_SCO private static final int FLAG_ALL = FLAG_AUDIBILITY_ENFORCED | FLAG_SECURE | FLAG_SCO
| FLAG_BEACON | FLAG_HW_AV_SYNC | FLAG_HW_HOTWORD | FLAG_BYPASS_INTERRUPTION_POLICY | FLAG_BEACON | FLAG_HW_AV_SYNC | FLAG_HW_HOTWORD | FLAG_BYPASS_INTERRUPTION_POLICY
| FLAG_BYPASS_MUTE | FLAG_LOW_LATENCY | FLAG_DEEP_BUFFER | FLAG_NO_MEDIA_PROJECTION | FLAG_BYPASS_MUTE | FLAG_LOW_LATENCY | FLAG_DEEP_BUFFER | FLAG_NO_MEDIA_PROJECTION
| FLAG_NO_SYSTEM_CAPTURE | FLAG_CAPTURE_PRIVATE | FLAG_CONTENT_SPATIALIZED | FLAG_NO_SYSTEM_CAPTURE | FLAG_CAPTURE_PRIVATE;
| FLAG_NEVER_SPATIALIZE;
private final static int FLAG_ALL_PUBLIC = FLAG_AUDIBILITY_ENFORCED | private final static int FLAG_ALL_PUBLIC = FLAG_AUDIBILITY_ENFORCED |
FLAG_HW_AV_SYNC | FLAG_LOW_LATENCY; FLAG_HW_AV_SYNC | FLAG_LOW_LATENCY;
/* mask of flags that can be set by SDK and System APIs through the Builder */ /* mask of flags that can be set by SDK and System APIs through the Builder */
@@ -627,49 +614,6 @@ public final class AudioAttributes implements Parcelable {
return (mFlags & FLAG_MUTE_HAPTIC) != 0; return (mFlags & FLAG_MUTE_HAPTIC) != 0;
} }
/**
* Return true if the audio content associated with these attributes has already been
* spatialized, that is it has already been processed to offer a binaural or transaural
* immersive audio experience.
* @return {@code true} if the content has been processed
*/
public boolean isContentSpatialized() {
return (mFlags & FLAG_CONTENT_SPATIALIZED) != 0;
}
/** @hide */
@IntDef(flag = false, value = {
SPATIALIZATION_BEHAVIOR_AUTO,
SPATIALIZATION_BEHAVIOR_NEVER,
})
@Retention(RetentionPolicy.SOURCE)
public @interface SpatializationBehavior {};
/**
* Constant indicating the audio content associated with these attributes will follow the
* default platform behavior with regards to which content will be spatialized or not.
* @see #getSpatializationBehavior()
* @see Spatializer
*/
public static final int SPATIALIZATION_BEHAVIOR_AUTO = 0;
/**
* Constant indicating the audio content associated with these attributes should never
* be virtualized.
* @see #getSpatializationBehavior()
* @see Spatializer
*/
public static final int SPATIALIZATION_BEHAVIOR_NEVER = 1;
/**
* Return the behavior affecting whether spatialization will be used.
* @return the spatialization behavior
*/
public @SpatializationBehavior int getSpatializationBehavior() {
return ((mFlags & FLAG_NEVER_SPATIALIZE) != 0)
? SPATIALIZATION_BEHAVIOR_NEVER : SPATIALIZATION_BEHAVIOR_AUTO;
}
/** /**
* Return the capture policy. * Return the capture policy.
* @return the capture policy set by {@link Builder#setAllowedCapturePolicy(int)} or * @return the capture policy set by {@link Builder#setAllowedCapturePolicy(int)} or
@@ -713,8 +657,6 @@ public final class AudioAttributes implements Parcelable {
private int mSource = MediaRecorder.AudioSource.AUDIO_SOURCE_INVALID; private int mSource = MediaRecorder.AudioSource.AUDIO_SOURCE_INVALID;
private int mFlags = 0x0; private int mFlags = 0x0;
private boolean mMuteHapticChannels = true; private boolean mMuteHapticChannels = true;
private boolean mIsContentSpatialized = false;
private int mSpatializationBehavior = SPATIALIZATION_BEHAVIOR_AUTO;
private HashSet<String> mTags = new HashSet<String>(); private HashSet<String> mTags = new HashSet<String>();
private Bundle mBundle; private Bundle mBundle;
private int mPrivacySensitive = PRIVACY_SENSITIVE_DEFAULT; private int mPrivacySensitive = PRIVACY_SENSITIVE_DEFAULT;
@@ -745,8 +687,6 @@ public final class AudioAttributes implements Parcelable {
mFlags = aa.getAllFlags(); mFlags = aa.getAllFlags();
mTags = (HashSet<String>) aa.mTags.clone(); mTags = (HashSet<String>) aa.mTags.clone();
mMuteHapticChannels = aa.areHapticChannelsMuted(); mMuteHapticChannels = aa.areHapticChannelsMuted();
mIsContentSpatialized = aa.isContentSpatialized();
mSpatializationBehavior = aa.getSpatializationBehavior();
} }
/** /**
@@ -779,12 +719,6 @@ public final class AudioAttributes implements Parcelable {
if (mMuteHapticChannels) { if (mMuteHapticChannels) {
aa.mFlags |= FLAG_MUTE_HAPTIC; aa.mFlags |= FLAG_MUTE_HAPTIC;
} }
if (mIsContentSpatialized) {
aa.mFlags |= FLAG_CONTENT_SPATIALIZED;
}
if (mSpatializationBehavior == SPATIALIZATION_BEHAVIOR_NEVER) {
aa.mFlags |= FLAG_NEVER_SPATIALIZE;
}
if (mPrivacySensitive == PRIVACY_SENSITIVE_DEFAULT) { if (mPrivacySensitive == PRIVACY_SENSITIVE_DEFAULT) {
// capturing for camcorder or communication is private by default to // capturing for camcorder or communication is private by default to
@@ -971,35 +905,6 @@ public final class AudioAttributes implements Parcelable {
return this; return this;
} }
/**
* Specifies whether the content has already been processed for spatialization.
* If it has, setting this to true will prevent issues such as double-processing.
* @param isSpatialized
* @return the same Builder instance
*/
public @NonNull Builder setIsContentSpatialized(boolean isSpatialized) {
mIsContentSpatialized = isSpatialized;
return this;
}
/**
* Sets the behavior affecting whether spatialization will be used.
* @param sb the spatialization behavior
* @return the same Builder instance
*
*/
public @NonNull Builder setSpatializationBehavior(@SpatializationBehavior int sb) {
switch (sb) {
case SPATIALIZATION_BEHAVIOR_NEVER:
case SPATIALIZATION_BEHAVIOR_AUTO:
break;
default:
throw new IllegalArgumentException("Invalid spatialization behavior " + sb);
}
mSpatializationBehavior = sb;
return this;
}
/** /**
* @hide * @hide
* Replaces flags. * Replaces flags.
@@ -1085,8 +990,6 @@ public final class AudioAttributes implements Parcelable {
mContentType = attributes.mContentType; mContentType = attributes.mContentType;
mFlags = attributes.getAllFlags(); mFlags = attributes.getAllFlags();
mMuteHapticChannels = attributes.areHapticChannelsMuted(); mMuteHapticChannels = attributes.areHapticChannelsMuted();
mIsContentSpatialized = attributes.isContentSpatialized();
mSpatializationBehavior = attributes.getSpatializationBehavior();
mTags = attributes.mTags; mTags = attributes.mTags;
mBundle = attributes.mBundle; mBundle = attributes.mBundle;
mSource = attributes.mSource; mSource = attributes.mSource;

View File

@@ -805,7 +805,7 @@ public class AudioManager {
} }
@UnsupportedAppUsage @UnsupportedAppUsage
static IAudioService getService() private static IAudioService getService()
{ {
if (sService != null) { if (sService != null) {
return sService; return sService;
@@ -2438,149 +2438,6 @@ public class AudioManager {
return AudioSystem.getOffloadSupport(format, attributes); return AudioSystem.getOffloadSupport(format, attributes);
} }
//====================================================================
// Immersive audio
/**
* @hide
* Returns the level of support for immersive audio from the {@link Spatializer} if
* available.
* @return the level of immersive audio support through spatialization
* @see Spatializer#getImmersiveAudioLevel()
*/
@Spatializer.ImmersiveAudioLevel int getSpatializerImmersiveAudioLevel() {
final IAudioService service = getService();
try {
return service.getSpatializerImmersiveAudioLevel();
} catch (RemoteException e) {
return Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
}
}
/**
* Return a handle to the optional platform's {@link Spatializer}
* @return {@code null} if spatialization is not supported, the {@code Spatializer} instance
* otherwise.
*/
public @Nullable Spatializer getSpatializer() {
if (getSpatializerImmersiveAudioLevel() == Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE) {
return null;
}
return new Spatializer(this);
}
/**
* @hide
* @see Spatializer#isEnabled()
* @return {@code true} if spatialization is enabled
*/
boolean isSpatializerEnabled() {
final IAudioService service = getService();
try {
return service.isSpatializerEnabled();
} catch (RemoteException e) {
Log.e(TAG, "Error querying isSpatializerEnabled, returning false", e);
return false;
}
}
/**
* @hide
* @see Spatializer#setEnabled(boolean)
* Enable/disable the spatialization wherever supported.
* @param enabled {@code true} to enable
*/
void setSpatializerFeatureEnabled(boolean enabled) {
final IAudioService service = getService();
try {
service.setSpatializerFeatureEnabled(enabled);
} catch (RemoteException e) {
Log.e(TAG, "Error calling setSpatializerFeatureEnabled", e);
}
}
/**
* @hide
* @see Spatializer#setEnabledForDevice(boolean, AudioDeviceAttributes)
* @see Spatializer#setEnabled(boolean)
* @param enabled enable/disable for a specific device.
* @param device the device concerned with spatializer functionality.
*/
void setSpatializerEnabledForDevice(boolean enabled,
@NonNull AudioDeviceAttributes device) {
final IAudioService service = getService();
try {
service.setSpatializerEnabledForDevice(enabled, device);
} catch (RemoteException e) {
Log.e(TAG, "Error calling setSpatializerEnabledForDevice", e);
}
}
/**
* @hide
* @see Spatializer#canBeSpatialized(AudioAttributes, AudioFormat)
* @param attributes the {@code AudioAttributes} of the content as used for playback
* @param format the {@code AudioFormat} of the content as used for playback
* @return true if the device is capable of spatializing the combination of audio
* format and attributes.
*/
boolean canBeSpatialized(
@NonNull AudioAttributes attributes, @NonNull AudioFormat format) {
final IAudioService service = getService();
try {
return service.canBeSpatialized(attributes, format);
} catch (RemoteException e) {
Log.e(TAG, "Error querying canBeSpatialized for attr:" + attributes
+ " format:" + format + " returning false", e);
return false;
}
}
/**
* @hide
* @see Spatializer#getCompatibleAudioDevices()
* @return a non-null list of the spatialization-compatible audio devices
*/
@NonNull List<AudioDeviceAttributes> getSpatializerCompatibleAudioDevices() {
final IAudioService service = getService();
try {
return service.getSpatializerCompatibleAudioDevices();
} catch (RemoteException e) {
Log.e(TAG, "Error querying getSpatializerCompatibleAudioDevices(), "
+ " returning empty list", e);
return new ArrayList<AudioDeviceAttributes>(0);
}
}
/**
* @hide
* @see Spatializer#addCompatibleAudioDevice(AudioDeviceAttributes)
* @param ada the audio device compatible with spatialization
*/
void addSpatializerCompatibleAudioDevice(@NonNull AudioDeviceAttributes ada) {
final IAudioService service = getService();
try {
service.addSpatializerCompatibleAudioDevice(ada);
} catch (RemoteException e) {
Log.e(TAG, "Error calling addSpatializerCompatibleAudioDevice()", e);
}
}
/**
* @hide
* @see Spatializer#removeCompatibleAudioDevice(AudioDeviceAttributes)
* @param ada the audio device incompatible with spatialization
*/
void removeSpatializerCompatibleAudioDevice(@NonNull AudioDeviceAttributes ada) {
final IAudioService service = getService();
try {
service.removeSpatializerCompatibleAudioDevice(ada);
} catch (RemoteException e) {
Log.e(TAG, "Error calling removeSpatializerCompatibleAudioDevice()", e);
}
}
//==================================================================== //====================================================================
// Bluetooth SCO control // Bluetooth SCO control
/** /**

View File

@@ -20,7 +20,6 @@ import android.bluetooth.BluetoothDevice;
import android.content.ComponentName; import android.content.ComponentName;
import android.media.AudioAttributes; import android.media.AudioAttributes;
import android.media.AudioDeviceAttributes; import android.media.AudioDeviceAttributes;
import android.media.AudioFormat;
import android.media.AudioFocusInfo; import android.media.AudioFocusInfo;
import android.media.AudioPlaybackConfiguration; import android.media.AudioPlaybackConfiguration;
import android.media.AudioRecordingConfiguration; import android.media.AudioRecordingConfiguration;
@@ -35,7 +34,6 @@ import android.media.IPlaybackConfigDispatcher;
import android.media.IRecordingConfigDispatcher; import android.media.IRecordingConfigDispatcher;
import android.media.IRingtonePlayer; import android.media.IRingtonePlayer;
import android.media.IStrategyPreferredDevicesDispatcher; import android.media.IStrategyPreferredDevicesDispatcher;
import android.media.ISpatializerCallback;
import android.media.IVolumeController; import android.media.IVolumeController;
import android.media.IVolumeController; import android.media.IVolumeController;
import android.media.PlayerBase; import android.media.PlayerBase;
@@ -394,24 +392,4 @@ interface IAudioService {
void registerModeDispatcher(IAudioModeDispatcher dispatcher); void registerModeDispatcher(IAudioModeDispatcher dispatcher);
oneway void unregisterModeDispatcher(IAudioModeDispatcher dispatcher); oneway void unregisterModeDispatcher(IAudioModeDispatcher dispatcher);
int getSpatializerImmersiveAudioLevel();
boolean isSpatializerEnabled();
void setSpatializerFeatureEnabled(boolean enabled);
void setSpatializerEnabledForDevice(boolean enabled, in AudioDeviceAttributes device);
boolean canBeSpatialized(in AudioAttributes aa, in AudioFormat af);
void registerSpatializerCallback(in ISpatializerCallback callback);
void unregisterSpatializerCallback(in ISpatializerCallback callback);
List<AudioDeviceAttributes> getSpatializerCompatibleAudioDevices();
void addSpatializerCompatibleAudioDevice(in AudioDeviceAttributes ada);
void removeSpatializerCompatibleAudioDevice(in AudioDeviceAttributes ada);
} }

View File

@@ -1,28 +0,0 @@
/*
* Copyright (C) 2021 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 the AudioService to signal Spatializer state changes.
*
* {@hide}
*/
oneway interface ISpatializerCallback {
void dispatchSpatializerStateChanged(boolean enabled);
}

View File

@@ -1,331 +0,0 @@
/*
* Copyright (C) 2021 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;
import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.media.permission.ClearCallingIdentityContext;
import android.media.permission.SafeCloseable;
import android.os.RemoteException;
import com.android.internal.annotations.GuardedBy;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Executor;
/**
* Spatializer provides access to querying capabilities and behavior of sound spatialization
* on the device.
* Sound spatialization simulates sounds originating around the listener as if they were coming
* from virtual speakers placed around the listener.<br>
* Support for spatialization is optional, use {@link AudioManager#getSpatializer()} to obtain an
* instance of this class if the feature is supported.
*
*/
public class Spatializer {
private final @NonNull AudioManager mAm;
private final Object mStateListenerLock = new Object();
/**
* List of listeners for state listener and their associated Executor.
* List is lazy-initialized on first registration
*/
@GuardedBy("mStateListenerLock")
private @Nullable ArrayList<StateListenerInfo> mStateListeners;
@GuardedBy("mStateListenerLock")
private SpatializerInfoDispatcherStub mInfoDispatcherStub;
/**
* @hide
* Constructor with AudioManager acting as proxy to AudioService
* @param am a non-null AudioManager
*/
protected Spatializer(@NonNull AudioManager am) {
mAm = Objects.requireNonNull(am);
}
/**
* Returns whether spatialization is enabled or not.
* A false value can originate from a number of sources, examples are the user electing to
* disable the feature, or the use of an audio device that is not compatible with multichannel
* audio spatialization (for instance playing audio over a monophonic speaker).
* @return {@code true} if spatialization is enabled
*/
public boolean isEnabled() {
return mAm.isSpatializerEnabled();
}
/** @hide */
@IntDef(flag = false, value = {
SPATIALIZER_IMMERSIVE_LEVEL_NONE,
SPATIALIZER_IMMERSIVE_LEVEL_MULTICHANNEL,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ImmersiveAudioLevel {};
/**
* @hide
* Constant indicating there are no spatialization capabilities supported on this device.
* @see AudioManager#getImmersiveAudioLevel()
*/
public static final int SPATIALIZER_IMMERSIVE_LEVEL_NONE = 0;
/**
* @hide
* Constant indicating the {@link Spatializer} on this device supports multichannel
* spatialization.
* @see AudioManager#getImmersiveAudioLevel()
*/
public static final int SPATIALIZER_IMMERSIVE_LEVEL_MULTICHANNEL = 1;
/**
* @hide
* @param enabled
* @param device
*/
//TODO make as API if needed for UX, remove otherwise
//@SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
//@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
public void setEnabledForDevice(boolean enabled,
@NonNull AudioDeviceAttributes device) {
Objects.requireNonNull(device);
mAm.setSpatializerEnabledForDevice(enabled, device);
}
/**
* @hide
* Enables / disables the spatializer effect
* @param enabled {@code true} for enabling the effect
*/
//TODO make as API if needed for UX, remove otherwise
//@SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
public void setEnabled(boolean enabled) {
mAm.setSpatializerFeatureEnabled(enabled);
}
/**
* An interface to be notified of changes to the state of the spatializer.
*/
public interface OnSpatializerEnabledChangedListener {
/**
* Called when the enabled state of the Spatializer changes
* @param enabled {@code true} if the Spatializer effect is enabled on the device,
* {@code false} otherwise
*/
void onSpatializerEnabledChanged(boolean enabled);
}
/**
* Returns whether audio of the given {@link AudioFormat}, played with the given
* {@link AudioAttributes} can be spatialized.
* Note that the result reflects the capabilities of the device and may change when
* audio accessories are connected/disconnected (e.g. wired headphones plugged in or not).
* The result is independent from whether spatialization processing is enabled or not.
* @param attributes the {@code AudioAttributes} of the content as used for playback
* @param format the {@code AudioFormat} of the content as used for playback
* @return true if the device is capable of spatializing the combination of audio format and
* attributes.
*/
public boolean canBeSpatialized(
@NonNull AudioAttributes attributes, @NonNull AudioFormat format) {
return mAm.canBeSpatialized(
Objects.requireNonNull(attributes), Objects.requireNonNull(format));
}
/**
* Adds a listener to be notified of changes to the enabled state of the
* {@code Spatializer}.
* @see #isEnabled()
* @param executor the {@code Executor} handling the callback
* @param listener the listener to receive enabled state updates
*/
public void addOnSpatializerEnabledChangedListener(
@NonNull @CallbackExecutor Executor executor,
@NonNull OnSpatializerEnabledChangedListener listener) {
Objects.requireNonNull(executor);
Objects.requireNonNull(listener);
synchronized (mStateListenerLock) {
if (hasSpatializerStateListener(listener)) {
throw new IllegalArgumentException(
"Called addOnSpatializerEnabledChangedListener() "
+ "on a previously registered listener");
}
// lazy initialization of the list of strategy-preferred device listener
if (mStateListeners == null) {
mStateListeners = new ArrayList<>();
}
mStateListeners.add(new StateListenerInfo(listener, executor));
if (mStateListeners.size() == 1) {
// register binder for callbacks
if (mInfoDispatcherStub == null) {
mInfoDispatcherStub =
new SpatializerInfoDispatcherStub();
}
try {
mAm.getService().registerSpatializerCallback(
mInfoDispatcherStub);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
}
}
/**
* Removes a previously added listener for changes to the enabled state of the
* {@code Spatializer}.
* @see #isEnabled()
* @param listener the listener to receive enabled state updates
*/
public void removeOnSpatializerEnabledChangedListener(
@NonNull OnSpatializerEnabledChangedListener listener) {
Objects.requireNonNull(listener);
synchronized (mStateListenerLock) {
if (!removeStateListener(listener)) {
throw new IllegalArgumentException(
"Called removeOnSpatializerEnabledChangedListener() "
+ "on an unregistered listener");
}
if (mStateListeners.size() == 0) {
// unregister binder for callbacks
try {
mAm.getService().unregisterSpatializerCallback(mInfoDispatcherStub);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
} finally {
mInfoDispatcherStub = null;
mStateListeners = null;
}
}
}
}
/**
* @hide
* Returns the list of playback devices that are compatible with the playback of multichannel
* audio through virtualization
* @return a list of devices. An empty list indicates virtualization is not supported.
*/
@SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
public @NonNull List<AudioDeviceAttributes> getCompatibleAudioDevices() {
return mAm.getSpatializerCompatibleAudioDevices();
}
/**
* @hide
* Adds a playback device to the list of devices compatible with the playback of multichannel
* audio through spatialization.
* @see #getCompatibleAudioDevices()
* @param ada the audio device compatible with spatialization
*/
@SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
public void addCompatibleAudioDevice(@NonNull AudioDeviceAttributes ada) {
mAm.addSpatializerCompatibleAudioDevice(Objects.requireNonNull(ada));
}
/**
* @hide
* Remove a playback device from the list of devices compatible with the playback of
* multichannel audio through spatialization.
* @see #getCompatibleAudioDevices()
* @param ada the audio device incompatible with spatialization
*/
@SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
public void removeCompatibleAudioDevice(@NonNull AudioDeviceAttributes ada) {
mAm.removeSpatializerCompatibleAudioDevice(Objects.requireNonNull(ada));
}
private final class SpatializerInfoDispatcherStub
extends ISpatializerCallback.Stub {
@Override
public void dispatchSpatializerStateChanged(boolean enabled) {
// make a shallow copy of listeners so callback is not executed under lock
final ArrayList<StateListenerInfo> stateListeners;
synchronized (mStateListenerLock) {
if (mStateListeners == null || mStateListeners.size() == 0) {
return;
}
stateListeners = (ArrayList<StateListenerInfo>) mStateListeners.clone();
}
try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
for (StateListenerInfo info : stateListeners) {
info.mExecutor.execute(() ->
info.mListener.onSpatializerEnabledChanged(enabled));
}
}
}
}
private static class StateListenerInfo {
final @NonNull OnSpatializerEnabledChangedListener mListener;
final @NonNull Executor mExecutor;
StateListenerInfo(@NonNull OnSpatializerEnabledChangedListener listener,
@NonNull Executor exe) {
mListener = listener;
mExecutor = exe;
}
}
@GuardedBy("mStateListenerLock")
private boolean hasSpatializerStateListener(OnSpatializerEnabledChangedListener listener) {
return getStateListenerInfo(listener) != null;
}
@GuardedBy("mStateListenerLock")
private @Nullable StateListenerInfo getStateListenerInfo(
OnSpatializerEnabledChangedListener listener) {
if (mStateListeners == null) {
return null;
}
for (StateListenerInfo info : mStateListeners) {
if (info.mListener == listener) {
return info;
}
}
return null;
}
@GuardedBy("mStateListenerLock")
/**
* @return true if the listener was removed from the list
*/
private boolean removeStateListener(OnSpatializerEnabledChangedListener listener) {
final StateListenerInfo infoToRemove = getStateListenerInfo(listener);
if (infoToRemove != null) {
mStateListeners.remove(infoToRemove);
return true;
}
return false;
}
}

View File

@@ -93,13 +93,11 @@ 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;
import android.media.ISpatializerCallback;
import android.media.IStrategyPreferredDevicesDispatcher; import android.media.IStrategyPreferredDevicesDispatcher;
import android.media.IVolumeController; import android.media.IVolumeController;
import android.media.MediaMetrics; import android.media.MediaMetrics;
import android.media.MediaRecorder.AudioSource; import android.media.MediaRecorder.AudioSource;
import android.media.PlayerBase; import android.media.PlayerBase;
import android.media.Spatializer;
import android.media.VolumePolicy; import android.media.VolumePolicy;
import android.media.audiofx.AudioEffect; import android.media.audiofx.AudioEffect;
import android.media.audiopolicy.AudioMix; import android.media.audiopolicy.AudioMix;
@@ -8226,82 +8224,6 @@ public class AudioService extends IAudioService.Stub
return true; return true;
} }
//==========================================================================================
private final SpatializerHelper mSpatializerHelper = new SpatializerHelper();
/** @see AudioManager#getSpatializerImmersiveAudioLevel() */
public int getSpatializerImmersiveAudioLevel() {
return Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
}
/** @see Spatializer#isEnabled() */
public boolean isSpatializerEnabled() {
return false;
}
/** @see Spatializer#canBeSpatialized() */
public boolean canBeSpatialized(
@NonNull AudioAttributes attributes, @NonNull AudioFormat format) {
Objects.requireNonNull(attributes);
Objects.requireNonNull(format);
return mSpatializerHelper.canBeSpatialized(attributes, format);
}
/** @see Spatializer.SpatializerInfoDispatcherStub */
public void registerSpatializerCallback(
@NonNull ISpatializerCallback dispatcher) {
Objects.requireNonNull(dispatcher);
mSpatializerHelper.registerStateCallback(dispatcher);
}
/** @see Spatializer.SpatializerInfoDispatcherStub */
public void unregisterSpatializerCallback(
@NonNull ISpatializerCallback dispatcher) {
Objects.requireNonNull(dispatcher);
mSpatializerHelper.unregisterStateCallback(dispatcher);
}
/** @see Spatializer#getSpatializerCompatibleAudioDevices() */
public @NonNull List<AudioDeviceAttributes> getSpatializerCompatibleAudioDevices() {
enforceModifyAudioRoutingPermission();
return mSpatializerHelper.getCompatibleAudioDevices();
}
/** @see Spatializer#addSpatializerCompatibleAudioDevice(AudioDeviceAttributes) */
public void addSpatializerCompatibleAudioDevice(@NonNull AudioDeviceAttributes ada) {
enforceModifyAudioRoutingPermission();
Objects.requireNonNull(ada);
mSpatializerHelper.addCompatibleAudioDevice(ada);
}
/** @see Spatializer#removeSpatializerCompatibleAudioDevice(AudioDeviceAttributes) */
public void removeSpatializerCompatibleAudioDevice(@NonNull AudioDeviceAttributes ada) {
enforceModifyAudioRoutingPermission();
Objects.requireNonNull(ada);
mSpatializerHelper.removeCompatibleAudioDevice(ada);
}
/** @see AudioManager#setSpatializerFeatureEnabled(boolean) */
public void setSpatializerFeatureEnabled(boolean enabled) {
if (mContext.checkCallingOrSelfPermission(
android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Missing MODIFY_DEFAULT_AUDIO_EFFECTS permission");
}
mSpatializerHelper.setEnabled(enabled);
}
/** @see Spatializer#setEnabledForDevice(boolean, AudioDeviceAttributes)
* @see Spatializer#setEnabled(boolean)
*/
public void setSpatializerEnabledForDevice(boolean enabled,
@NonNull AudioDeviceAttributes ada) {
enforceModifyAudioRoutingPermission();
Objects.requireNonNull(ada);
mSpatializerHelper.setEnabledForDevice(enabled, ada);
}
//========================================================================================== //==========================================================================================
private boolean readCameraSoundForced() { private boolean readCameraSoundForced() {
return SystemProperties.getBoolean("audio.camerasound.force", false) || return SystemProperties.getBoolean("audio.camerasound.force", false) ||

View File

@@ -1,126 +0,0 @@
/*
* Copyright (C) 2021 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 com.android.server.audio;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.media.AudioAttributes;
import android.media.AudioDeviceAttributes;
import android.media.AudioFormat;
import android.media.ISpatializerCallback;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.util.Log;
import java.util.ArrayList;
import java.util.List;
/**
* A helper class to manage Spatializer related functionality
*/
public class SpatializerHelper {
private static final String TAG = "AS.NotificationHelper";
//---------------------------------------------------------------
// audio device compatibility / enabled
private final ArrayList<AudioDeviceAttributes> mCompatibleAudioDevices = new ArrayList<>(0);
private final ArrayList<AudioDeviceAttributes> mEnabledAudioDevices = new ArrayList<>(0);
/**
* @return a shallow copy of the list of compatible audio devices
*/
synchronized @NonNull List<AudioDeviceAttributes> getCompatibleAudioDevices() {
return (List<AudioDeviceAttributes>) mCompatibleAudioDevices.clone();
}
synchronized void addCompatibleAudioDevice(@NonNull AudioDeviceAttributes ada) {
if (!mCompatibleAudioDevices.contains(ada)) {
mCompatibleAudioDevices.add(ada);
}
// by default, adding a compatible device enables it
if (!mEnabledAudioDevices.contains(ada)) {
mEnabledAudioDevices.add(ada);
}
}
synchronized void removeCompatibleAudioDevice(@NonNull AudioDeviceAttributes ada) {
mCompatibleAudioDevices.remove(ada);
mEnabledAudioDevices.remove(ada);
}
synchronized void setEnabledForDevice(boolean enabled, @NonNull AudioDeviceAttributes ada) {
if (enabled) {
if (!mEnabledAudioDevices.contains(ada)) {
mEnabledAudioDevices.add(ada);
}
} else {
mEnabledAudioDevices.remove(ada);
}
}
//------------------------------------------------------
// enabled state
// global state of feature
boolean mFeatureEnabled = false;
synchronized void setEnabled(boolean enabled) {
final boolean oldState = mFeatureEnabled;
mFeatureEnabled = enabled;
if (oldState != enabled) {
dispatchState();
}
}
final RemoteCallbackList<ISpatializerCallback> mStateCallbacks =
new RemoteCallbackList<ISpatializerCallback>();
synchronized void registerStateCallback(
@NonNull ISpatializerCallback callback) {
mStateCallbacks.register(callback);
}
synchronized void unregisterStateCallback(
@NonNull ISpatializerCallback callback) {
mStateCallbacks.unregister(callback);
}
private synchronized void dispatchState() {
// TODO check enabled state based on available devices
// (current implementation takes state as-is and dispatches it to listeners
final int nbCallbacks = mStateCallbacks.beginBroadcast();
for (int i = 0; i < nbCallbacks; i++) {
try {
mStateCallbacks.getBroadcastItem(i)
.dispatchSpatializerStateChanged(mFeatureEnabled);
} catch (RemoteException e) {
Log.e(TAG, "Error in dispatchSpatializerStateChanged", e);
}
}
mStateCallbacks.finishBroadcast();
}
//------------------------------------------------------
// virtualization capabilities
synchronized boolean canBeSpatialized(
@NonNull AudioAttributes attributes, @NonNull AudioFormat format) {
// TODO hook up to spatializer effect for query
return false;
}
}