Merge "Spatializer: add API for monitoring output changes" into sc-v2-dev

This commit is contained in:
Jean-Michel Trivi
2021-10-13 03:43:04 +00:00
committed by Android (Google) Code Review
6 changed files with 243 additions and 0 deletions

View File

@@ -5377,10 +5377,12 @@ package android.media {
method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public void addCompatibleAudioDevice(@NonNull android.media.AudioDeviceAttributes);
method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public void addOnHeadTrackingModeChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.Spatializer.OnHeadTrackingModeChangedListener);
method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public void clearOnHeadToSoundstagePoseUpdatedListener();
method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public void clearOnSpatializerOutputChangedListener();
method @NonNull @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public java.util.List<android.media.AudioDeviceAttributes> getCompatibleAudioDevices();
method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public int getDesiredHeadTrackingMode();
method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public void getEffectParameter(int, @NonNull byte[]);
method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public int getHeadTrackingMode();
method @IntRange(from=0) @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public int getOutput();
method @NonNull @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public java.util.List<java.lang.Integer> getSupportedHeadTrackingModes();
method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public void recenterHeadTracker();
method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public void removeCompatibleAudioDevice(@NonNull android.media.AudioDeviceAttributes);
@@ -5390,6 +5392,7 @@ package android.media {
method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public void setEnabled(boolean);
method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public void setGlobalTransform(@NonNull float[]);
method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public void setOnHeadToSoundstagePoseUpdatedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.Spatializer.OnHeadToSoundstagePoseUpdatedListener);
method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public void setOnSpatializerOutputChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.Spatializer.OnSpatializerOutputChangedListener);
field @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public static final int HEAD_TRACKING_MODE_DISABLED = -1; // 0xffffffff
field @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public static final int HEAD_TRACKING_MODE_OTHER = 0; // 0x0
field @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public static final int HEAD_TRACKING_MODE_RELATIVE_DEVICE = 2; // 0x2
@@ -5406,6 +5409,10 @@ package android.media {
method public void onHeadTrackingModeChanged(@NonNull android.media.Spatializer, int);
}
public static interface Spatializer.OnSpatializerOutputChangedListener {
method public void onSpatializerOutputChanged(@NonNull android.media.Spatializer, @IntRange(from=0) int);
}
}
package android.media.audiofx {

View File

@@ -38,6 +38,7 @@ import android.media.IStrategyPreferredDevicesDispatcher;
import android.media.ISpatializerCallback;
import android.media.ISpatializerHeadTrackingModeCallback;
import android.media.ISpatializerHeadToSoundStagePoseCallback;
import android.media.ISpatializerOutputCallback;
import android.media.IVolumeController;
import android.media.IVolumeController;
import android.media.PlayerBase;
@@ -440,4 +441,10 @@ interface IAudioService {
void setSpatializerParameter(int key, in byte[] value);
void getSpatializerParameter(int key, inout byte[] value);
int getSpatializerOutput();
void registerSpatializerOutputCallback(in ISpatializerOutputCallback cb);
void unregisterSpatializerOutputCallback(in ISpatializerOutputCallback cb);
}

View File

@@ -0,0 +1,27 @@
/*
* 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 output changes.
*
* {@hide}
*/
oneway interface ISpatializerOutputCallback {
void dispatchSpatializerOutputChanged(int output);
}

View File

@@ -18,6 +18,7 @@ package android.media;
import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -298,6 +299,24 @@ public class Spatializer {
@HeadTrackingModeSet int mode);
}
/**
* @hide
* An interface to be notified of changes to the output stream used by the spatializer
* effect.
* @see #getOutput()
*/
@SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
public interface OnSpatializerOutputChangedListener {
/**
* Called when the id of the output stream of the spatializer effect changed.
* @param spatializer the {@code Spatializer} instance whose output is updated
* @param output the id of the output stream, or 0 when there is no spatializer output
*/
void onSpatializerOutputChanged(@NonNull Spatializer spatializer,
@IntRange(from = 0) int output);
}
/**
* @hide
* An interface to be notified of updates to the head to soundstage pose, as represented by the
@@ -844,6 +863,73 @@ public class Spatializer {
}
}
/**
* @hide
* Returns the id of the output stream used for the spatializer effect playback
* @return id of the output stream, or 0 if no spatializer playback is active
*/
@SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
@RequiresPermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
public @IntRange(from = 0) int getOutput() {
try {
return mAm.getService().getSpatializerOutput();
} catch (RemoteException e) {
Log.e(TAG, "Error calling getSpatializerOutput", e);
return 0;
}
}
/**
* @hide
* Sets the listener to receive spatializer effect output updates
* @param executor the {@code Executor} handling the callbacks
* @param listener the listener to register
* @see #clearOnSpatializerOutputChangedListener()
* @see #getOutput()
*/
@SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
@RequiresPermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
public void setOnSpatializerOutputChangedListener(
@NonNull @CallbackExecutor Executor executor,
@NonNull OnSpatializerOutputChangedListener listener) {
Objects.requireNonNull(executor);
Objects.requireNonNull(listener);
synchronized (mOutputListenerLock) {
if (mOutputListener != null) {
throw new IllegalStateException("Trying to overwrite existing listener");
}
mOutputListener =
new ListenerInfo<OnSpatializerOutputChangedListener>(listener, executor);
mOutputDispatcher = new SpatializerOutputDispatcherStub();
try {
mAm.getService().registerSpatializerOutputCallback(mOutputDispatcher);
} catch (RemoteException e) {
mOutputListener = null;
mOutputDispatcher = null;
}
}
}
/**
* @hide
* Clears the listener for spatializer effect output updates
* @see #setOnSpatializerOutputChangedListener(Executor, OnSpatializerOutputChangedListener)
*/
@SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
@RequiresPermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
public void clearOnSpatializerOutputChangedListener() {
synchronized (mOutputListenerLock) {
if (mOutputDispatcher == null) {
throw (new IllegalStateException("No listener to clear"));
}
try {
mAm.getService().unregisterSpatializerOutputCallback(mOutputDispatcher);
} catch (RemoteException e) { }
mOutputListener = null;
mOutputDispatcher = null;
}
}
//-----------------------------------------------------------------------------
// callback helper definitions
@@ -969,4 +1055,35 @@ public class Spatializer {
}
}
}
//-----------------------------------------------------------------------------
// output callback management and stub
private final Object mOutputListenerLock = new Object();
/**
* Listener for output updates
*/
@GuardedBy("mOutputListenerLock")
private @Nullable ListenerInfo<OnSpatializerOutputChangedListener> mOutputListener;
@GuardedBy("mOutputListenerLock")
private @Nullable SpatializerOutputDispatcherStub mOutputDispatcher;
private final class SpatializerOutputDispatcherStub
extends ISpatializerOutputCallback.Stub {
@Override
public void dispatchSpatializerOutputChanged(int output) {
// make a copy of ref to listener so callback is not executed under lock
final ListenerInfo<OnSpatializerOutputChangedListener> listener;
synchronized (mOutputListenerLock) {
listener = mOutputListener;
}
if (listener == null) {
return;
}
try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
listener.mExecutor.execute(() -> listener.mListener
.onSpatializerOutputChanged(Spatializer.this, output));
}
}
}
}

View File

@@ -96,6 +96,7 @@ import android.media.IRingtonePlayer;
import android.media.ISpatializerCallback;
import android.media.ISpatializerHeadToSoundStagePoseCallback;
import android.media.ISpatializerHeadTrackingModeCallback;
import android.media.ISpatializerOutputCallback;
import android.media.IStrategyPreferredDevicesDispatcher;
import android.media.IVolumeController;
import android.media.MediaMetrics;
@@ -8498,6 +8499,26 @@ public class AudioService extends IAudioService.Stub
mSpatializerHelper.getEffectParameter(key, value);
}
/** @see Spatializer#getOutput */
public int getSpatializerOutput() {
enforceModifyDefaultAudioEffectsPermission();
return mSpatializerHelper.getOutput();
}
/** @see Spatializer#setOnSpatializerOutputChangedListener */
public void registerSpatializerOutputCallback(ISpatializerOutputCallback cb) {
enforceModifyDefaultAudioEffectsPermission();
Objects.requireNonNull(cb);
mSpatializerHelper.registerSpatializerOutputCallback(cb);
}
/** @see Spatializer#clearOnSpatializerOutputChangedListener */
public void unregisterSpatializerOutputCallback(ISpatializerOutputCallback cb) {
enforceModifyDefaultAudioEffectsPermission();
Objects.requireNonNull(cb);
mSpatializerHelper.unregisterSpatializerOutputCallback(cb);
}
/**
* post a message to schedule init/release of head tracking sensors
* @param init initialization if true, release if false

View File

@@ -31,6 +31,7 @@ import android.media.ISpatializerCallback;
import android.media.ISpatializerHeadToSoundStagePoseCallback;
import android.media.ISpatializerHeadTrackingCallback;
import android.media.ISpatializerHeadTrackingModeCallback;
import android.media.ISpatializerOutputCallback;
import android.media.SpatializationLevel;
import android.media.Spatializer;
import android.media.SpatializerHeadTrackingMode;
@@ -76,6 +77,7 @@ public class SpatializerHelper {
private int mCapableSpatLevel = Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
private int mActualHeadTrackingMode = Spatializer.HEAD_TRACKING_MODE_UNSUPPORTED;
private int mDesiredHeadTrackingMode = Spatializer.HEAD_TRACKING_MODE_UNSUPPORTED;
private int mSpatOutput = 0;
private @Nullable ISpatializer mSpat;
private @Nullable SpatializerCallback mSpatCallback;
private @Nullable SpatializerHeadTrackingCallback mSpatHeadTrackingCallback;
@@ -216,6 +218,14 @@ public class SpatializerHelper {
public void onOutputChanged(int output) {
logd("SpatializerCallback.onOutputChanged output:" + output);
int oldOutput;
synchronized (SpatializerHelper.this) {
oldOutput = mSpatOutput;
mSpatOutput = output;
}
if (oldOutput != output) {
dispatchOutputUpdate(output);
}
}
};
@@ -785,6 +795,60 @@ public class SpatializerHelper {
}
}
//------------------------------------------------------
// output
/** @see Spatializer#getOutput */
synchronized int getOutput() {
switch (mState) {
case STATE_UNINITIALIZED:
case STATE_NOT_SUPPORTED:
throw (new IllegalStateException(
"Can't get output without a spatializer"));
case STATE_ENABLED_UNAVAILABLE:
case STATE_DISABLED_UNAVAILABLE:
case STATE_DISABLED_AVAILABLE:
case STATE_ENABLED_AVAILABLE:
if (mSpat == null) {
throw (new IllegalStateException(
"null Spatializer for getOutput"));
}
break;
}
// mSpat != null
try {
return mSpat.getOutput();
} catch (RemoteException e) {
Log.e(TAG, "Error in getOutput", e);
return 0;
}
}
final RemoteCallbackList<ISpatializerOutputCallback> mOutputCallbacks =
new RemoteCallbackList<ISpatializerOutputCallback>();
synchronized void registerSpatializerOutputCallback(
@NonNull ISpatializerOutputCallback callback) {
mOutputCallbacks.register(callback);
}
synchronized void unregisterSpatializerOutputCallback(
@NonNull ISpatializerOutputCallback callback) {
mOutputCallbacks.unregister(callback);
}
private void dispatchOutputUpdate(int output) {
final int nbCallbacks = mOutputCallbacks.beginBroadcast();
for (int i = 0; i < nbCallbacks; i++) {
try {
mOutputCallbacks.getBroadcastItem(i).dispatchSpatializerOutputChanged(output);
} catch (RemoteException e) {
Log.e(TAG, "Error in dispatchOutputUpdate", e);
}
}
mOutputCallbacks.finishBroadcast();
}
//------------------------------------------------------
// sensors
private void initSensors(boolean init) {