mirror of
https://github.com/androidx/media.git
synced 2025-04-29 22:36:54 +08:00
Add util to obtain AudioManager
AudioManager internally assumes the thread is was created on can be used as a callback thread. Since the AudioManager is only created once in the lifetime of an app, we need to make sure its obtained on a thread that stays alive. #cherrypick PiperOrigin-RevId: 732072255 (cherry picked from commit 2088697a19ac85feb26ba2521a70647803246571)
This commit is contained in:
parent
595fd48cd7
commit
766dbd497e
@ -15,12 +15,18 @@
|
||||
*/
|
||||
package androidx.media3.common.audio;
|
||||
|
||||
import static androidx.media3.common.util.Assertions.checkNotNull;
|
||||
import static java.lang.annotation.ElementType.TYPE_USE;
|
||||
|
||||
import android.content.Context;
|
||||
import android.media.AudioManager;
|
||||
import android.os.Looper;
|
||||
import androidx.annotation.IntDef;
|
||||
import androidx.annotation.IntRange;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.media3.common.C;
|
||||
import androidx.media3.common.util.BackgroundExecutor;
|
||||
import androidx.media3.common.util.ConditionVariable;
|
||||
import androidx.media3.common.util.Log;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import androidx.media3.common.util.Util;
|
||||
@ -28,11 +34,14 @@ import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
|
||||
/** Compatibility layer for {@link AudioManager} with fallbacks for older Android versions. */
|
||||
@UnstableApi
|
||||
public final class AudioManagerCompat {
|
||||
|
||||
private static final String TAG = "AudioManagerCompat";
|
||||
|
||||
/**
|
||||
* Audio focus gain types. One of {@link #AUDIOFOCUS_NONE}, {@link #AUDIOFOCUS_GAIN}, {@link
|
||||
* #AUDIOFOCUS_GAIN_TRANSIENT}, {@link #AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK} or {@link
|
||||
@ -83,6 +92,55 @@ public final class AudioManagerCompat {
|
||||
public static final int AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE =
|
||||
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE;
|
||||
|
||||
@SuppressWarnings("NonFinalStaticField") // Lazily initialized under class lock
|
||||
@Nullable
|
||||
private static AudioManager audioManager;
|
||||
|
||||
@SuppressWarnings("NonFinalStaticField") // Lazily initialized under class lock
|
||||
private static @MonotonicNonNull Context applicationContext;
|
||||
|
||||
/**
|
||||
* Returns the {@link AudioManager}.
|
||||
*
|
||||
* <p>This method avoids potential threading issues where AudioManager keeps access to the thread
|
||||
* it was created on until after this thread is stopped.
|
||||
*
|
||||
* <p>It is recommended to use this method from a background thread.
|
||||
*
|
||||
* @param context A {@link Context}.
|
||||
* @return The {@link AudioManager}.
|
||||
*/
|
||||
public static synchronized AudioManager getAudioManager(Context context) {
|
||||
Context applicationContext = context.getApplicationContext();
|
||||
if (AudioManagerCompat.applicationContext != applicationContext) {
|
||||
// Reset cached instance if the application context changed. This should only happen in tests.
|
||||
audioManager = null;
|
||||
}
|
||||
if (audioManager != null) {
|
||||
return audioManager;
|
||||
}
|
||||
@Nullable Looper myLooper = Looper.myLooper();
|
||||
if (myLooper == null || myLooper == Looper.getMainLooper()) {
|
||||
// The AudioManager will assume the main looper as default callback anyway, so create the
|
||||
// instance here without using BackgroundExecutor.
|
||||
audioManager = (AudioManager) applicationContext.getSystemService(Context.AUDIO_SERVICE);
|
||||
return checkNotNull(audioManager);
|
||||
}
|
||||
// Create the audio manager on the BackgroundExecutor to avoid running the potentially blocking
|
||||
// command on the main thread but still use a thread that is guaranteed to exist for the
|
||||
// lifetime of the app.
|
||||
ConditionVariable audioManagerSetCondition = new ConditionVariable();
|
||||
BackgroundExecutor.get()
|
||||
.execute(
|
||||
() -> {
|
||||
audioManager =
|
||||
(AudioManager) applicationContext.getSystemService(Context.AUDIO_SERVICE);
|
||||
audioManagerSetCondition.open();
|
||||
});
|
||||
audioManagerSetCondition.blockUninterruptible();
|
||||
return checkNotNull(audioManager);
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests audio focus. See the {@link AudioFocusRequestCompat} for information about the options
|
||||
* available to configure your request, and notification of focus gain and loss.
|
||||
|
@ -31,6 +31,8 @@ public final class BackgroundExecutor {
|
||||
*
|
||||
* <p>Must only be used for quick, high-priority tasks to ensure other background tasks are not
|
||||
* blocked.
|
||||
*
|
||||
* <p>The thread is guaranteed to be alive for the lifetime of the application.
|
||||
*/
|
||||
public static synchronized Executor get() {
|
||||
if (staticInstance == null) {
|
||||
@ -42,6 +44,9 @@ public final class BackgroundExecutor {
|
||||
/**
|
||||
* Sets the {@link Executor} to be returned from {@link #get()}.
|
||||
*
|
||||
* <p>Note that the thread of the provided {@link Executor} must stay alive for the lifetime of
|
||||
* the application.
|
||||
*
|
||||
* @param executor An {@link Executor} that runs tasks on background threads and should only be
|
||||
* used for quick, high-priority tasks to ensure other background tasks are not blocked.
|
||||
*/
|
||||
|
@ -95,6 +95,7 @@ import androidx.media3.common.ParserException;
|
||||
import androidx.media3.common.PlaybackException;
|
||||
import androidx.media3.common.Player;
|
||||
import androidx.media3.common.Player.Commands;
|
||||
import androidx.media3.common.audio.AudioManagerCompat;
|
||||
import androidx.media3.common.audio.AudioProcessor;
|
||||
import com.google.common.base.Ascii;
|
||||
import com.google.common.io.ByteStreams;
|
||||
@ -2485,9 +2486,7 @@ public final class Util {
|
||||
*/
|
||||
@UnstableApi
|
||||
public static int generateAudioSessionIdV21(Context context) {
|
||||
@Nullable
|
||||
AudioManager audioManager = ((AudioManager) context.getSystemService(Context.AUDIO_SERVICE));
|
||||
return audioManager == null ? AudioManager.ERROR : audioManager.generateAudioSessionId();
|
||||
return AudioManagerCompat.getAudioManager(context).generateAudioSessionId();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -26,6 +26,7 @@ import androidx.media3.common.AudioAttributes;
|
||||
import androidx.media3.common.C;
|
||||
import androidx.media3.common.Format;
|
||||
import androidx.media3.common.MimeTypes;
|
||||
import androidx.media3.common.audio.AudioManagerCompat;
|
||||
import androidx.media3.common.util.TraceUtil;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import androidx.media3.common.util.Util;
|
||||
@ -95,10 +96,7 @@ public class LibiamfAudioRenderer extends DecoderAudioRenderer<IamfDecoder> {
|
||||
return false;
|
||||
}
|
||||
|
||||
AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
|
||||
if (audioManager == null) {
|
||||
return false;
|
||||
}
|
||||
AudioManager audioManager = AudioManagerCompat.getAudioManager(context);
|
||||
AudioFormat audioFormat =
|
||||
new AudioFormat.Builder()
|
||||
.setEncoding(IamfDecoder.OUTPUT_PCM_ENCODING)
|
||||
|
@ -142,12 +142,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
* @param playerControl A {@link PlayerControl} to handle commands from this instance.
|
||||
*/
|
||||
public AudioFocusManager(Context context, Looper eventLooper, PlayerControl playerControl) {
|
||||
this.audioManager =
|
||||
Suppliers.memoize(
|
||||
() ->
|
||||
checkNotNull(
|
||||
(AudioManager)
|
||||
context.getApplicationContext().getSystemService(Context.AUDIO_SERVICE)));
|
||||
this.audioManager = Suppliers.memoize(() -> AudioManagerCompat.getAudioManager(context));
|
||||
this.playerControl = playerControl;
|
||||
this.eventHandler = new Handler(eventLooper);
|
||||
this.audioFocusState = AUDIO_FOCUS_STATE_NOT_REQUESTED;
|
||||
|
@ -40,6 +40,7 @@ import androidx.media3.common.AudioAttributes;
|
||||
import androidx.media3.common.C;
|
||||
import androidx.media3.common.Format;
|
||||
import androidx.media3.common.MimeTypes;
|
||||
import androidx.media3.common.audio.AudioManagerCompat;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import androidx.media3.common.util.Util;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
@ -142,8 +143,7 @@ public final class AudioCapabilities {
|
||||
@Nullable Intent intent,
|
||||
AudioAttributes audioAttributes,
|
||||
@Nullable AudioDeviceInfoApi23 routedDevice) {
|
||||
AudioManager audioManager =
|
||||
(AudioManager) checkNotNull(context.getSystemService(Context.AUDIO_SERVICE));
|
||||
AudioManager audioManager = AudioManagerCompat.getAudioManager(context);
|
||||
AudioDeviceInfoApi23 currentDevice =
|
||||
routedDevice != null
|
||||
? routedDevice
|
||||
|
@ -31,6 +31,7 @@ import android.os.Handler;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.media3.common.AudioAttributes;
|
||||
import androidx.media3.common.audio.AudioManagerCompat;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import androidx.media3.common.util.Util;
|
||||
import java.util.Objects;
|
||||
@ -261,14 +262,12 @@ public final class AudioCapabilitiesReceiver {
|
||||
|
||||
public static void registerAudioDeviceCallback(
|
||||
Context context, AudioDeviceCallback callback, Handler handler) {
|
||||
AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
|
||||
checkNotNull(audioManager).registerAudioDeviceCallback(callback, handler);
|
||||
AudioManagerCompat.getAudioManager(context).registerAudioDeviceCallback(callback, handler);
|
||||
}
|
||||
|
||||
public static void unregisterAudioDeviceCallback(
|
||||
Context context, AudioDeviceCallback callback) {
|
||||
AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
|
||||
checkNotNull(audioManager).unregisterAudioDeviceCallback(callback);
|
||||
AudioManagerCompat.getAudioManager(context).unregisterAudioDeviceCallback(callback);
|
||||
}
|
||||
|
||||
private Api23() {}
|
||||
|
@ -26,6 +26,7 @@ import androidx.media3.common.AudioAttributes;
|
||||
import androidx.media3.common.C;
|
||||
import androidx.media3.common.Format;
|
||||
import androidx.media3.common.MimeTypes;
|
||||
import androidx.media3.common.audio.AudioManagerCompat;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import androidx.media3.common.util.Util;
|
||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
@ -116,17 +117,13 @@ public final class DefaultAudioOffloadSupportProvider
|
||||
}
|
||||
|
||||
if (context != null) {
|
||||
AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
|
||||
if (audioManager != null) {
|
||||
String offloadVariableRateSupportedKeyValue =
|
||||
audioManager.getParameters(/* keys= */ OFFLOAD_VARIABLE_RATE_SUPPORTED_KEY);
|
||||
isOffloadVariableRateSupported =
|
||||
offloadVariableRateSupportedKeyValue != null
|
||||
&& offloadVariableRateSupportedKeyValue.equals(
|
||||
OFFLOAD_VARIABLE_RATE_SUPPORTED_KEY + "=1");
|
||||
} else {
|
||||
isOffloadVariableRateSupported = false;
|
||||
}
|
||||
AudioManager audioManager = AudioManagerCompat.getAudioManager(context);
|
||||
String offloadVariableRateSupportedKeyValue =
|
||||
audioManager.getParameters(/* keys= */ OFFLOAD_VARIABLE_RATE_SUPPORTED_KEY);
|
||||
isOffloadVariableRateSupported =
|
||||
offloadVariableRateSupportedKeyValue != null
|
||||
&& offloadVariableRateSupportedKeyValue.equals(
|
||||
OFFLOAD_VARIABLE_RATE_SUPPORTED_KEY + "=1");
|
||||
} else {
|
||||
isOffloadVariableRateSupported = false;
|
||||
}
|
||||
|
@ -54,6 +54,7 @@ import androidx.media3.common.TrackGroup;
|
||||
import androidx.media3.common.TrackSelectionOverride;
|
||||
import androidx.media3.common.TrackSelectionParameters;
|
||||
import androidx.media3.common.TrackSelectionParameters.AudioOffloadPreferences;
|
||||
import androidx.media3.common.audio.AudioManagerCompat;
|
||||
import androidx.media3.common.util.Assertions;
|
||||
import androidx.media3.common.util.BundleCollectionUtil;
|
||||
import androidx.media3.common.util.Log;
|
||||
@ -4280,7 +4281,8 @@ public class DefaultTrackSelector extends MappingTrackSelector
|
||||
@Nullable Context context, DefaultTrackSelector defaultTrackSelector) {
|
||||
@Nullable
|
||||
AudioManager audioManager =
|
||||
context == null ? null : (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
|
||||
context == null ? null : AudioManagerCompat.getAudioManager(context);
|
||||
;
|
||||
if (audioManager == null || Util.isTv(checkNotNull(context))) {
|
||||
spatializer = null;
|
||||
spatializationSupported = false;
|
||||
|
@ -66,6 +66,7 @@ import androidx.annotation.IntDef;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.annotation.RestrictTo;
|
||||
import androidx.media3.common.audio.AudioManagerCompat;
|
||||
import androidx.media3.common.util.NullableType;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import androidx.media3.session.legacy.MediaSessionManager.RemoteUserInfo;
|
||||
@ -2335,7 +2336,7 @@ public class MediaSessionCompat {
|
||||
}
|
||||
mContext = context;
|
||||
mSessionInfo = sessionInfo;
|
||||
mAudioManager = (AudioManager) checkNotNull(context.getSystemService(Context.AUDIO_SERVICE));
|
||||
mAudioManager = AudioManagerCompat.getAudioManager(context);
|
||||
mMediaButtonReceiverComponentName = mbrComponent;
|
||||
mMediaButtonReceiverIntent = mbrIntent;
|
||||
mStub = new MediaSessionStub(/* mediaSessionImpl= */ this, context.getPackageName(), tag);
|
||||
|
Loading…
x
Reference in New Issue
Block a user