Commit 7c265dfb by olly Committed by Oliver Woodman

Detect external surround sound on Amazon devices

PiperOrigin-RevId: 241544595
parent 9b064fb9
...@@ -22,7 +22,10 @@ import android.content.Intent; ...@@ -22,7 +22,10 @@ import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.media.AudioFormat; import android.media.AudioFormat;
import android.media.AudioManager; import android.media.AudioManager;
import android.net.Uri;
import android.provider.Settings.Global;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.google.android.exoplayer2.util.Util;
import java.util.Arrays; import java.util.Arrays;
/** Represents the set of audio formats that a device is capable of playing. */ /** Represents the set of audio formats that a device is capable of playing. */
...@@ -35,6 +38,17 @@ public final class AudioCapabilities { ...@@ -35,6 +38,17 @@ public final class AudioCapabilities {
public static final AudioCapabilities DEFAULT_AUDIO_CAPABILITIES = public static final AudioCapabilities DEFAULT_AUDIO_CAPABILITIES =
new AudioCapabilities(new int[] {AudioFormat.ENCODING_PCM_16BIT}, DEFAULT_MAX_CHANNEL_COUNT); new AudioCapabilities(new int[] {AudioFormat.ENCODING_PCM_16BIT}, DEFAULT_MAX_CHANNEL_COUNT);
/** Audio capabilities when the device specifies external surround sound. */
private static final AudioCapabilities EXTERNAL_SURROUND_SOUND_CAPABILITIES =
new AudioCapabilities(
new int[] {
AudioFormat.ENCODING_PCM_16BIT, AudioFormat.ENCODING_AC3, AudioFormat.ENCODING_E_AC3
},
DEFAULT_MAX_CHANNEL_COUNT);
/** Global settings key for devices that can specify external surround sound. */
private static final String EXTERNAL_SURROUND_SOUND_KEY = "external_surround_sound_enabled";
/** /**
* Returns the current audio capabilities for the device. * Returns the current audio capabilities for the device.
* *
...@@ -43,12 +57,18 @@ public final class AudioCapabilities { ...@@ -43,12 +57,18 @@ public final class AudioCapabilities {
*/ */
@SuppressWarnings("InlinedApi") @SuppressWarnings("InlinedApi")
public static AudioCapabilities getCapabilities(Context context) { public static AudioCapabilities getCapabilities(Context context) {
return getCapabilities( Intent intent =
context.registerReceiver(null, new IntentFilter(AudioManager.ACTION_HDMI_AUDIO_PLUG))); context.registerReceiver(
/* receiver= */ null, new IntentFilter(AudioManager.ACTION_HDMI_AUDIO_PLUG));
return getCapabilities(context, intent);
} }
@SuppressLint("InlinedApi") @SuppressLint("InlinedApi")
/* package */ static AudioCapabilities getCapabilities(@Nullable Intent intent) { /* package */ static AudioCapabilities getCapabilities(Context context, @Nullable Intent intent) {
if (deviceMaySetExternalSurroundSoundGlobalSetting()
&& Global.getInt(context.getContentResolver(), EXTERNAL_SURROUND_SOUND_KEY, 0) == 1) {
return EXTERNAL_SURROUND_SOUND_CAPABILITIES;
}
if (intent == null || intent.getIntExtra(AudioManager.EXTRA_AUDIO_PLUG_STATE, 0) == 0) { if (intent == null || intent.getIntExtra(AudioManager.EXTRA_AUDIO_PLUG_STATE, 0) == 0) {
return DEFAULT_AUDIO_CAPABILITIES; return DEFAULT_AUDIO_CAPABILITIES;
} }
...@@ -58,6 +78,17 @@ public final class AudioCapabilities { ...@@ -58,6 +78,17 @@ public final class AudioCapabilities {
AudioManager.EXTRA_MAX_CHANNEL_COUNT, /* defaultValue= */ DEFAULT_MAX_CHANNEL_COUNT)); AudioManager.EXTRA_MAX_CHANNEL_COUNT, /* defaultValue= */ DEFAULT_MAX_CHANNEL_COUNT));
} }
/**
* Returns the global settings {@link Uri} used by the device to specify external surround sound,
* or null if the device does not support this functionality.
*/
@Nullable
/* package */ static Uri getExternalSurroundSoundGlobalSettingUri() {
return deviceMaySetExternalSurroundSoundGlobalSetting()
? Global.getUriFor(EXTERNAL_SURROUND_SOUND_KEY)
: null;
}
private final int[] supportedEncodings; private final int[] supportedEncodings;
private final int maxChannelCount; private final int maxChannelCount;
...@@ -124,4 +155,7 @@ public final class AudioCapabilities { ...@@ -124,4 +155,7 @@ public final class AudioCapabilities {
+ ", supportedEncodings=" + Arrays.toString(supportedEncodings) + "]"; + ", supportedEncodings=" + Arrays.toString(supportedEncodings) + "]";
} }
private static boolean deviceMaySetExternalSurroundSoundGlobalSetting() {
return Util.SDK_INT >= 17 && "Amazon".equals(Util.MANUFACTURER);
}
} }
...@@ -16,10 +16,13 @@ ...@@ -16,10 +16,13 @@
package com.google.android.exoplayer2.audio; package com.google.android.exoplayer2.audio;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.database.ContentObserver;
import android.media.AudioManager; import android.media.AudioManager;
import android.net.Uri;
import android.os.Handler; import android.os.Handler;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
...@@ -46,31 +49,30 @@ public final class AudioCapabilitiesReceiver { ...@@ -46,31 +49,30 @@ public final class AudioCapabilitiesReceiver {
} }
private final Context context; private final Context context;
private final @Nullable Handler handler;
private final Listener listener; private final Listener listener;
private final @Nullable BroadcastReceiver receiver; private final Handler handler;
@Nullable private final BroadcastReceiver receiver;
@Nullable private final ExternalSurroundSoundSettingObserver externalSurroundSoundSettingObserver;
/* package */ @Nullable AudioCapabilities audioCapabilities; /* package */ @Nullable AudioCapabilities audioCapabilities;
private boolean registered;
/** /**
* @param context A context for registering the receiver. * @param context A context for registering the receiver.
* @param listener The listener to notify when audio capabilities change. * @param listener The listener to notify when audio capabilities change.
*/ */
public AudioCapabilitiesReceiver(Context context, Listener listener) { public AudioCapabilitiesReceiver(Context context, Listener listener) {
this(context, /* handler= */ null, listener); context = context.getApplicationContext();
} this.context = context;
/**
* @param context A context for registering the receiver.
* @param handler The handler to which {@link Listener} events will be posted. If null, listener
* methods are invoked on the main thread.
* @param listener The listener to notify when audio capabilities change.
*/
public AudioCapabilitiesReceiver(Context context, @Nullable Handler handler, Listener listener) {
this.context = Assertions.checkNotNull(context);
this.handler = handler;
this.listener = Assertions.checkNotNull(listener); this.listener = Assertions.checkNotNull(listener);
this.receiver = Util.SDK_INT >= 21 ? new HdmiAudioPlugBroadcastReceiver() : null; handler = new Handler(Util.getLooper());
receiver = Util.SDK_INT >= 21 ? new HdmiAudioPlugBroadcastReceiver() : null;
Uri externalSurroundSoundUri = AudioCapabilities.getExternalSurroundSoundGlobalSettingUri();
externalSurroundSoundSettingObserver =
externalSurroundSoundUri != null
? new ExternalSurroundSoundSettingObserver(
handler, context.getContentResolver(), externalSurroundSoundUri)
: null;
} }
/** /**
...@@ -82,18 +84,21 @@ public final class AudioCapabilitiesReceiver { ...@@ -82,18 +84,21 @@ public final class AudioCapabilitiesReceiver {
*/ */
@SuppressWarnings("InlinedApi") @SuppressWarnings("InlinedApi")
public AudioCapabilities register() { public AudioCapabilities register() {
if (registered) {
return Assertions.checkNotNull(audioCapabilities);
}
registered = true;
if (externalSurroundSoundSettingObserver != null) {
externalSurroundSoundSettingObserver.register();
}
Intent stickyIntent = null; Intent stickyIntent = null;
if (receiver != null) { if (receiver != null) {
IntentFilter intentFilter = new IntentFilter(AudioManager.ACTION_HDMI_AUDIO_PLUG); IntentFilter intentFilter = new IntentFilter(AudioManager.ACTION_HDMI_AUDIO_PLUG);
if (handler != null) { stickyIntent =
stickyIntent = context.registerReceiver(
context.registerReceiver( receiver, intentFilter, /* broadcastPermission= */ null, handler);
receiver, intentFilter, /* broadcastPermission= */ null, handler);
} else {
stickyIntent = context.registerReceiver(receiver, intentFilter);
}
} }
audioCapabilities = AudioCapabilities.getCapabilities(stickyIntent); audioCapabilities = AudioCapabilities.getCapabilities(context, stickyIntent);
return audioCapabilities; return audioCapabilities;
} }
...@@ -102,9 +107,24 @@ public final class AudioCapabilitiesReceiver { ...@@ -102,9 +107,24 @@ public final class AudioCapabilitiesReceiver {
* changes occur. * changes occur.
*/ */
public void unregister() { public void unregister() {
if (!registered) {
return;
}
audioCapabilities = null;
if (receiver != null) { if (receiver != null) {
context.unregisterReceiver(receiver); context.unregisterReceiver(receiver);
} }
if (externalSurroundSoundSettingObserver != null) {
externalSurroundSoundSettingObserver.unregister();
}
registered = false;
}
private void onNewAudioCapabilities(AudioCapabilities newAudioCapabilities) {
if (registered && !newAudioCapabilities.equals(audioCapabilities)) {
audioCapabilities = newAudioCapabilities;
listener.onAudioCapabilitiesChanged(newAudioCapabilities);
}
} }
private final class HdmiAudioPlugBroadcastReceiver extends BroadcastReceiver { private final class HdmiAudioPlugBroadcastReceiver extends BroadcastReceiver {
...@@ -112,14 +132,35 @@ public final class AudioCapabilitiesReceiver { ...@@ -112,14 +132,35 @@ public final class AudioCapabilitiesReceiver {
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
if (!isInitialStickyBroadcast()) { if (!isInitialStickyBroadcast()) {
AudioCapabilities newAudioCapabilities = AudioCapabilities.getCapabilities(intent); onNewAudioCapabilities(AudioCapabilities.getCapabilities(context, intent));
if (!newAudioCapabilities.equals(audioCapabilities)) {
audioCapabilities = newAudioCapabilities;
listener.onAudioCapabilitiesChanged(newAudioCapabilities);
}
} }
} }
}
private final class ExternalSurroundSoundSettingObserver extends ContentObserver {
private final ContentResolver resolver;
private final Uri settingUri;
public ExternalSurroundSoundSettingObserver(
Handler handler, ContentResolver resolver, Uri settingUri) {
super(handler);
this.resolver = resolver;
this.settingUri = settingUri;
}
public void register() {
resolver.registerContentObserver(settingUri, /* notifyForDescendants= */ false, this);
}
public void unregister() {
resolver.unregisterContentObserver(this);
}
@Override
public void onChange(boolean selfChange) {
onNewAudioCapabilities(AudioCapabilities.getCapabilities(context));
}
} }
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment