summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Adrian Roos <roosa@google.com> 2024-02-05 13:45:32 +0000
committer Adrian Roos <roosa@google.com> 2024-02-05 14:10:37 +0000
commit4cac51712bfd5347710b7449cc652d9808246bec (patch)
treeee055def8672dfd293d97aaf6fba73de1b16b536
parentb18a2588b7f8503e5c1db12d1aa4bc20390605bd (diff)
Revert "Add back the older Ringtone implementation to allow flag control."
This reverts commit 549920720ffc7b29adc13041492d6116fa391077. Bug: 323853450 Change-Id: Iceadf80ea342580c160106bf0b31588ef73406ea
-rw-r--r--media/java/android/media/IRingtonePlayer.aidl12
-rw-r--r--media/java/android/media/LocalRingtonePlayer.java10
-rw-r--r--media/java/android/media/Ringtone.java527
-rw-r--r--media/java/android/media/RingtoneManager.java59
-rw-r--r--media/java/android/media/RingtoneV1.java614
-rw-r--r--media/java/android/media/RingtoneV2.java690
-rw-r--r--packages/SoundPicker2/src/com/android/soundpicker/RingtoneFactory.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java69
8 files changed, 439 insertions, 1550 deletions
diff --git a/media/java/android/media/IRingtonePlayer.aidl b/media/java/android/media/IRingtonePlayer.aidl
index 1e57be2c1e22..c515a399a141 100644
--- a/media/java/android/media/IRingtonePlayer.aidl
+++ b/media/java/android/media/IRingtonePlayer.aidl
@@ -30,20 +30,12 @@ interface IRingtonePlayer {
/** Used for Ringtone.java playback */
@UnsupportedAppUsage
oneway void play(IBinder token, in Uri uri, in AudioAttributes aa, float volume, boolean looping);
- oneway void stop(IBinder token);
- boolean isPlaying(IBinder token);
-
- // RingtoneV1
- oneway void playWithVolumeShaping(IBinder token, in Uri uri, in AudioAttributes aa,
- float volume, boolean looping, in @nullable VolumeShaper.Configuration volumeShaperConfig);
- oneway void setPlaybackProperties(IBinder token, float volume, boolean looping,
- boolean hapticGeneratorEnabled);
-
- // RingtoneV2
oneway void playRemoteRingtone(IBinder token, in Uri uri, in AudioAttributes aa,
boolean useExactAudioAttributes, int enabledMedia, in @nullable VibrationEffect ve,
float volume, boolean looping, boolean hapticGeneratorEnabled,
in @nullable VolumeShaper.Configuration volumeShaperConfig);
+ oneway void stop(IBinder token);
+ boolean isPlaying(IBinder token);
oneway void setLooping(IBinder token, boolean looping);
oneway void setVolume(IBinder token, float volume);
oneway void setHapticGeneratorEnabled(IBinder token, boolean hapticGeneratorEnabled);
diff --git a/media/java/android/media/LocalRingtonePlayer.java b/media/java/android/media/LocalRingtonePlayer.java
index fe7cc3ec2af3..d0169b9e2501 100644
--- a/media/java/android/media/LocalRingtonePlayer.java
+++ b/media/java/android/media/LocalRingtonePlayer.java
@@ -37,7 +37,7 @@ import java.util.Objects;
* @hide
*/
public class LocalRingtonePlayer
- implements RingtoneV2.RingtonePlayer, MediaPlayer.OnCompletionListener {
+ implements Ringtone.RingtonePlayer, MediaPlayer.OnCompletionListener {
private static final String TAG = "LocalRingtonePlayer";
// keep references on active Ringtones until stopped or completion listener called.
@@ -45,7 +45,7 @@ public class LocalRingtonePlayer
private final MediaPlayer mMediaPlayer;
private final AudioAttributes mAudioAttributes;
- private final RingtoneV2.RingtonePlayer mVibrationPlayer;
+ private final Ringtone.RingtonePlayer mVibrationPlayer;
private final Ringtone.Injectables mInjectables;
private final AudioManager mAudioManager;
private final VolumeShaper mVolumeShaper;
@@ -55,7 +55,7 @@ public class LocalRingtonePlayer
@NonNull AudioAttributes audioAttributes, @NonNull Ringtone.Injectables injectables,
@NonNull AudioManager audioManager, @Nullable HapticGenerator hapticGenerator,
@Nullable VolumeShaper volumeShaper,
- @Nullable RingtoneV2.RingtonePlayer vibrationPlayer) {
+ @Nullable Ringtone.RingtonePlayer vibrationPlayer) {
Objects.requireNonNull(mediaPlayer);
Objects.requireNonNull(audioAttributes);
Objects.requireNonNull(injectables);
@@ -74,7 +74,7 @@ public class LocalRingtonePlayer
* loaded in the local player.
*/
@Nullable
- static RingtoneV2.RingtonePlayer create(@NonNull Context context,
+ static Ringtone.RingtonePlayer create(@NonNull Context context,
@NonNull AudioManager audioManager, @NonNull Vibrator vibrator,
@NonNull Uri soundUri,
@NonNull AudioAttributes audioAttributes,
@@ -311,7 +311,7 @@ public class LocalRingtonePlayer
}
/** A RingtonePlayer that only plays a VibrationEffect. */
- static class VibrationEffectPlayer implements RingtoneV2.RingtonePlayer {
+ static class VibrationEffectPlayer implements Ringtone.RingtonePlayer {
private static final int VIBRATION_LOOP_DELAY_MS = 200;
private final VibrationEffect mVibrationEffect;
private final VibrationAttributes mVibrationAttributes;
diff --git a/media/java/android/media/Ringtone.java b/media/java/android/media/Ringtone.java
index 0319f32521c1..3a6b39834851 100644
--- a/media/java/android/media/Ringtone.java
+++ b/media/java/android/media/Ringtone.java
@@ -25,11 +25,14 @@ import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.content.res.AssetFileDescriptor;
+import android.content.res.Resources.NotFoundException;
import android.database.Cursor;
import android.media.audiofx.HapticGenerator;
import android.net.Uri;
+import android.os.Binder;
+import android.os.IBinder;
import android.os.RemoteException;
-import android.os.SystemProperties;
import android.os.Trace;
import android.os.VibrationEffect;
import android.os.Vibrator;
@@ -40,6 +43,7 @@ import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
+import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -96,70 +100,68 @@ public class Ringtone {
private static final String MEDIA_SELECTION = MediaColumns.MIME_TYPE + " LIKE 'audio/%' OR "
+ MediaColumns.MIME_TYPE + " IN ('application/ogg', 'application/x-flac')";
- // Flag-selected ringtone implementation to use.
- private final ApiInterface mApiImpl;
-
- /** {@hide} */
- @UnsupportedAppUsage
- public Ringtone(Context context, boolean allowRemote) {
- mApiImpl = new RingtoneV1(context, allowRemote);
- }
+ private final Context mContext;
+ private final Vibrator mVibrator;
+ private final AudioManager mAudioManager;
+ private VolumeShaper.Configuration mVolumeShaperConfig;
/**
- * Constructor for legacy V1 initialization paths using non-public APIs on RingtoneV1.
+ * Flag indicating if we're allowed to fall back to remote playback using
+ * {@link #mRemoteRingtoneService}. Typically this is false when we're the remote
+ * player and there is nobody else to delegate to.
*/
- private Ringtone(RingtoneV1 ringtoneV1) {
- mApiImpl = ringtoneV1;
- }
+ private final boolean mAllowRemote;
+ private final IRingtonePlayer mRemoteRingtoneService;
+ private final Injectables mInjectables;
+
+ private final int mEnabledMedia;
+
+ private final Uri mUri;
+ private String mTitle;
+
+ private AudioAttributes mAudioAttributes;
+ private boolean mUseExactAudioAttributes;
+ private boolean mPreferBuiltinDevice;
+ private RingtonePlayer mActivePlayer;
+ // playback properties, use synchronized with mPlaybackSettingsLock
+ private boolean mIsLooping;
+ private float mVolume;
+ private boolean mHapticGeneratorEnabled;
+ private final Object mPlaybackSettingsLock = new Object();
+ private final VibrationEffect mVibrationEffect;
private Ringtone(Builder builder, @Ringtone.RingtoneMedia int effectiveEnabledMedia,
@NonNull AudioAttributes effectiveAudioAttributes,
@Nullable VibrationEffect effectiveVibrationEffect,
boolean effectiveHapticGeneratorEnabled) {
- mApiImpl = new RingtoneV2(builder.mContext, builder.mInjectables, builder.mAllowRemote,
- effectiveEnabledMedia, builder.mUri, effectiveAudioAttributes,
- builder.mUseExactAudioAttributes, builder.mVolumeShaperConfig,
- builder.mPreferBuiltinDevice, builder.mInitialSoundVolume, builder.mLooping,
- effectiveHapticGeneratorEnabled, effectiveVibrationEffect);
- }
-
- /**
- * Temporary V1 constructor for legacy V1 paths with audio attributes.
- * @hide
- */
- public static Ringtone createV1WithCustomAudioAttributes(
- Context context, AudioAttributes audioAttributes, Uri uri,
- VolumeShaper.Configuration volumeShaperConfig, boolean allowRemote) {
- RingtoneV1 ringtoneV1 = new RingtoneV1(context, allowRemote);
- ringtoneV1.setAudioAttributesField(audioAttributes);
- ringtoneV1.setUri(uri, volumeShaperConfig);
- ringtoneV1.reinitializeActivePlayer();
- return new Ringtone(ringtoneV1);
- }
-
- /**
- * Temporary V1 constructor for legacy V1 paths with stream type.
- * @hide
- */
- public static Ringtone createV1WithCustomStreamType(
- Context context, int streamType, Uri uri,
- VolumeShaper.Configuration volumeShaperConfig) {
- RingtoneV1 ringtoneV1 = new RingtoneV1(context, /* allowRemote= */ true);
- if (streamType >= 0) {
- ringtoneV1.setStreamType(streamType);
- }
- ringtoneV1.setUri(uri, volumeShaperConfig);
- if (!ringtoneV1.reinitializeActivePlayer()) {
- Log.e(TAG, "Failed to open ringtone " + uri);
- return null;
- }
- return new Ringtone(ringtoneV1);
+ // Context
+ mContext = builder.mContext;
+ mInjectables = builder.mInjectables;
+ //mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+ mAudioManager = mContext.getSystemService(AudioManager.class);
+ mRemoteRingtoneService = builder.mAllowRemote ? mAudioManager.getRingtonePlayer() : null;
+ mVibrator = mContext.getSystemService(Vibrator.class);
+
+ // Local-only (not propagated to remote).
+ mPreferBuiltinDevice = builder.mPreferBuiltinDevice; // System-only
+ mAllowRemote = (mRemoteRingtoneService != null); // Always false for remote.
+
+ // Properties potentially propagated to remote player.
+ mEnabledMedia = effectiveEnabledMedia;
+ mUri = builder.mUri;
+ mVolumeShaperConfig = builder.mVolumeShaperConfig;
+ mVolume = builder.mInitialSoundVolume;
+ mIsLooping = builder.mLooping;
+ mVibrationEffect = effectiveVibrationEffect;
+ mAudioAttributes = effectiveAudioAttributes;
+ mUseExactAudioAttributes = builder.mUseExactAudioAttributes;
+ mHapticGeneratorEnabled = effectiveHapticGeneratorEnabled;
}
/** @hide */
@RingtoneMedia
public int getEnabledMedia() {
- return mApiImpl.getEnabledMedia();
+ return mEnabledMedia;
}
/**
@@ -170,7 +172,15 @@ public class Ringtone {
*/
@Deprecated
public void setStreamType(int streamType) {
- mApiImpl.setStreamType(streamType);
+ setAudioAttributes(
+ getAudioAttributesForLegacyStreamType(streamType, "setStreamType()"));
+ }
+
+ private AudioAttributes getAudioAttributesForLegacyStreamType(int streamType, String originOp) {
+ PlayerBase.deprecateStreamTypeForPlayback(streamType, "Ringtone", originOp);
+ return new AudioAttributes.Builder()
+ .setInternalLegacyStreamType(streamType)
+ .build();
}
/**
@@ -182,7 +192,7 @@ public class Ringtone {
*/
@Deprecated
public int getStreamType() {
- return mApiImpl.getStreamType();
+ return AudioAttributes.toLegacyStreamType(mAudioAttributes);
}
/**
@@ -191,7 +201,17 @@ public class Ringtone {
*/
public void setAudioAttributes(AudioAttributes attributes)
throws IllegalArgumentException {
- mApiImpl.setAudioAttributes(attributes);
+ // TODO: deprecate this method - it will be done with a builder.
+ if (attributes == null) {
+ throw new IllegalArgumentException("Invalid null AudioAttributes for Ringtone");
+ }
+ mAudioAttributes = attributes;
+ // Setting the audio attributes requires re-initializing the player.
+ if (mActivePlayer != null) {
+ // The audio attributes have to be set before the media player is prepared.
+ // Re-initialize it.
+ reinitializeActivePlayer();
+ }
}
/**
@@ -201,19 +221,19 @@ public class Ringtone {
*/
@Nullable
public VibrationEffect getVibrationEffect() {
- return mApiImpl.getVibrationEffect();
+ return mVibrationEffect;
}
/** @hide */
@VisibleForTesting
public boolean getPreferBuiltinDevice() {
- return mApiImpl.getPreferBuiltinDevice();
+ return mPreferBuiltinDevice;
}
/** @hide */
@VisibleForTesting
public VolumeShaper.Configuration getVolumeShaperConfig() {
- return mApiImpl.getVolumeShaperConfig();
+ return mVolumeShaperConfig;
}
/**
@@ -223,13 +243,31 @@ public class Ringtone {
*/
@VisibleForTesting
public boolean isLocalOnly() {
- return mApiImpl.isLocalOnly();
+ return !mAllowRemote;
}
/** @hide */
@VisibleForTesting
public boolean isUsingRemotePlayer() {
- return mApiImpl.isUsingRemotePlayer();
+ return mActivePlayer instanceof RemoteRingtonePlayer;
+ }
+
+ /**
+ * Finds the output device of type {@link AudioDeviceInfo#TYPE_BUILTIN_SPEAKER}. This device is
+ * the one on which outgoing audio for SIM calls is played.
+ *
+ * @param audioManager the audio manage.
+ * @return the {@link AudioDeviceInfo} corresponding to the builtin device, or {@code null} if
+ * none can be found.
+ */
+ private AudioDeviceInfo getBuiltinDevice(AudioManager audioManager) {
+ AudioDeviceInfo[] deviceList = audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS);
+ for (AudioDeviceInfo device : deviceList) {
+ if (device.getType() == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER) {
+ return device;
+ }
+ }
+ return null;
}
/**
@@ -239,7 +277,84 @@ public class Ringtone {
* @hide
*/
public boolean reinitializeActivePlayer() {
- return mApiImpl.reinitializeActivePlayer();
+ // Try creating a local media player, or fallback to creating a remote one.
+ Trace.beginSection("reinitializeActivePlayer");
+ try {
+ if (mActivePlayer != null) {
+ // This would only happen if calling the deprecated setAudioAttributes after
+ // building the Ringtone.
+ stopAndReleaseActivePlayer();
+ }
+
+ boolean vibrationOnly = (mEnabledMedia & MEDIA_ALL) == MEDIA_VIBRATION;
+ // Vibration can come from the audio file if using haptic generator or if haptic
+ // channels are a possibility.
+ boolean maybeAudioVibration = mUri != null && mInjectables.isHapticPlaybackSupported()
+ && (mHapticGeneratorEnabled || !mAudioAttributes.areHapticChannelsMuted());
+
+ // VibrationEffect only, use the simplified player without checking for haptic channels.
+ if (vibrationOnly && !maybeAudioVibration && mVibrationEffect != null) {
+ mActivePlayer = new LocalRingtonePlayer.VibrationEffectPlayer(
+ mVibrationEffect, mAudioAttributes, mVibrator, mIsLooping);
+ return true;
+ }
+
+ AudioDeviceInfo preferredDevice =
+ mPreferBuiltinDevice ? getBuiltinDevice(mAudioManager) : null;
+ if (mUri != null) {
+ mActivePlayer = LocalRingtonePlayer.create(mContext, mAudioManager, mVibrator, mUri,
+ mAudioAttributes, vibrationOnly, mVibrationEffect, mInjectables,
+ mVolumeShaperConfig, preferredDevice, mHapticGeneratorEnabled, mIsLooping,
+ mVolume);
+ } else {
+ // Using the remote player won't help play a null Uri. Revert straight to fallback.
+ // The vibration-only case was already covered above.
+ mActivePlayer = createFallbackRingtonePlayer();
+ // Fall through to attempting remote fallback play if null.
+ }
+
+ if (mActivePlayer == null && mAllowRemote) {
+ mActivePlayer = new RemoteRingtonePlayer(mRemoteRingtoneService, mUri,
+ mAudioAttributes, mUseExactAudioAttributes, mEnabledMedia, mVibrationEffect,
+ mVolumeShaperConfig, mHapticGeneratorEnabled, mIsLooping, mVolume);
+ }
+
+ return mActivePlayer != null;
+ } finally {
+ Trace.endSection();
+ }
+ }
+
+ @Nullable
+ private LocalRingtonePlayer createFallbackRingtonePlayer() {
+ int ringtoneType = RingtoneManager.getDefaultType(mUri);
+ if (ringtoneType != -1
+ && RingtoneManager.getActualDefaultRingtoneUri(mContext, ringtoneType) == null) {
+ Log.w(TAG, "not playing fallback for " + mUri);
+ return null;
+ }
+ // Default ringtone, try fallback ringtone.
+ try (AssetFileDescriptor afd = mContext.getResources().openRawResourceFd(
+ com.android.internal.R.raw.fallbackring)) {
+ if (afd == null) {
+ Log.e(TAG, "Could not load fallback ringtone");
+ return null;
+ }
+
+ AudioDeviceInfo preferredDevice =
+ mPreferBuiltinDevice ? getBuiltinDevice(mAudioManager) : null;
+ return LocalRingtonePlayer.createForFallback(mAudioManager, mVibrator, afd,
+ mAudioAttributes, mVibrationEffect, mInjectables, mVolumeShaperConfig,
+ preferredDevice, mIsLooping, mVolume);
+ } catch (NotFoundException nfe) {
+ Log.e(TAG, "Fallback ringtone does not exist");
+ return null;
+ } catch (IOException e) {
+ // As with the above messages, not including much information about the
+ // failure so as not to expose details of the fallback ringtone resource.
+ Log.e(TAG, "Exception reading fallback ringtone");
+ return null;
+ }
}
/**
@@ -247,7 +362,7 @@ public class Ringtone {
* @hide
*/
public boolean hasHapticChannels() {
- return mApiImpl.hasHapticChannels();
+ return (mActivePlayer == null) ? false : mActivePlayer.hasHapticChannels();
}
/**
@@ -256,7 +371,7 @@ public class Ringtone {
* {@link #setAudioAttributes(AudioAttributes)} or the default attributes if none were set.
*/
public AudioAttributes getAudioAttributes() {
- return mApiImpl.getAudioAttributes();
+ return mAudioAttributes;
}
/**
@@ -264,7 +379,12 @@ public class Ringtone {
* @param looping whether to loop or not.
*/
public void setLooping(boolean looping) {
- mApiImpl.setLooping(looping);
+ synchronized (mPlaybackSettingsLock) {
+ mIsLooping = looping;
+ if (mActivePlayer != null) {
+ mActivePlayer.setLooping(looping);
+ }
+ }
}
/**
@@ -272,7 +392,9 @@ public class Ringtone {
* @return true if this player loops when playing.
*/
public boolean isLooping() {
- return mApiImpl.isLooping();
+ synchronized (mPlaybackSettingsLock) {
+ return mIsLooping;
+ }
}
/**
@@ -281,7 +403,22 @@ public class Ringtone {
* corresponds to no attenuation being applied.
*/
public void setVolume(float volume) {
- mApiImpl.setVolume(volume);
+ // Ignore if sound not enabled.
+ if ((mEnabledMedia & MEDIA_SOUND) == 0) {
+ return;
+ }
+ if (volume < 0.0f) {
+ volume = 0.0f;
+ } else if (volume > 1.0f) {
+ volume = 1.0f;
+ }
+
+ synchronized (mPlaybackSettingsLock) {
+ mVolume = volume;
+ if (mActivePlayer != null) {
+ mActivePlayer.setVolume(volume);
+ }
+ }
}
/**
@@ -289,7 +426,9 @@ public class Ringtone {
* @return a value between 0.0f and 1.0f.
*/
public float getVolume() {
- return mApiImpl.getVolume();
+ synchronized (mPlaybackSettingsLock) {
+ return mVolume;
+ }
}
/**
@@ -300,7 +439,16 @@ public class Ringtone {
* @see android.media.audiofx.HapticGenerator#isAvailable()
*/
public boolean setHapticGeneratorEnabled(boolean enabled) {
- return mApiImpl.setHapticGeneratorEnabled(enabled);
+ if (!mInjectables.isHapticGeneratorAvailable()) {
+ return false;
+ }
+ synchronized (mPlaybackSettingsLock) {
+ mHapticGeneratorEnabled = enabled;
+ if (mActivePlayer != null) {
+ mActivePlayer.setHapticGeneratorEnabled(enabled);
+ }
+ }
+ return true;
}
/**
@@ -308,7 +456,9 @@ public class Ringtone {
* @return true if the HapticGenerator is enabled.
*/
public boolean isHapticGeneratorEnabled() {
- return mApiImpl.isHapticGeneratorEnabled();
+ synchronized (mPlaybackSettingsLock) {
+ return mHapticGeneratorEnabled;
+ }
}
/**
@@ -318,7 +468,8 @@ public class Ringtone {
* @param context A context used for querying.
*/
public String getTitle(Context context) {
- return mApiImpl.getTitle(context);
+ if (mTitle != null) return mTitle;
+ return mTitle = getTitle(context, mUri, true /*followSettingsUri*/, mAllowRemote);
}
/**
@@ -395,21 +546,38 @@ public class Ringtone {
/** {@hide} */
@UnsupportedAppUsage
public Uri getUri() {
- return mApiImpl.getUri();
+ return mUri;
}
/**
* Plays the ringtone.
*/
public void play() {
- mApiImpl.play();
+ if (mActivePlayer != null) {
+ if (mActivePlayer.play()) {
+ return;
+ } else {
+ // Discard active player: play() is only meant to be called once.
+ stopAndReleaseActivePlayer();
+ }
+ }
+ if (!playFallbackRingtone()) {
+ Log.w(TAG, "Neither local nor remote playback available");
+ }
}
/**
* Stops a playing ringtone.
*/
public void stop() {
- mApiImpl.stop();
+ stopAndReleaseActivePlayer();
+ }
+
+ private void stopAndReleaseActivePlayer() {
+ if (mActivePlayer != null) {
+ mActivePlayer.stopAndRelease();
+ mActivePlayer = null;
+ }
}
/**
@@ -418,7 +586,41 @@ public class Ringtone {
* @return True if playing, false otherwise.
*/
public boolean isPlaying() {
- return mApiImpl.isPlaying();
+ if (mActivePlayer != null) {
+ return mActivePlayer.isPlaying();
+ } else {
+ Log.w(TAG, "No active ringtone player");
+ return false;
+ }
+ }
+
+ /**
+ * Fallback during the play stage rather than initialization, typically due to an issue
+ * communicating with the remote player.
+ */
+ private boolean playFallbackRingtone() {
+ if (mActivePlayer != null) {
+ Log.wtf(TAG, "Playing fallback ringtone with another active player");
+ stopAndReleaseActivePlayer();
+ }
+ int streamType = AudioAttributes.toLegacyStreamType(mAudioAttributes);
+ if (mAudioManager.getStreamVolume(streamType) == 0) {
+ // TODO: Return true? If volume is off, this is a successful play.
+ return false;
+ }
+ mActivePlayer = createFallbackRingtonePlayer();
+ if (mActivePlayer == null) {
+ return false; // the create method logs if it returns null.
+ } else if (mActivePlayer.play()) {
+ return true;
+ } else {
+ stopAndReleaseActivePlayer();
+ return false;
+ }
+ }
+
+ void setTitle(String title) {
+ mTitle = title;
}
/**
@@ -685,6 +887,140 @@ public class Ringtone {
}
/**
+ * Play a specific ringtone. This interface is implemented by either local (this process) or
+ * proxied-remote playback via AudioManager.getRingtonePlayer, so that the caller
+ * (Ringtone class) can just use a single player after the initial creation.
+ * @hide
+ */
+ interface RingtonePlayer {
+ /**
+ * Start playing the ringtone, returning false if there was a problem that
+ * requires falling back to the fallback ringtone resource.
+ */
+ boolean play();
+ boolean isPlaying();
+ void stopAndRelease();
+
+ // Mutating playback methods.
+ void setPreferredDevice(@Nullable AudioDeviceInfo audioDeviceInfo);
+ void setLooping(boolean looping);
+ void setHapticGeneratorEnabled(boolean enabled);
+ void setVolume(float volume);
+
+ boolean hasHapticChannels();
+ }
+
+ /**
+ * Remote RingtonePlayer. All operations are delegated via the IRingtonePlayer interface, which
+ * should ultimately be backed by a RingtoneLocalPlayer within the system services.
+ */
+ static class RemoteRingtonePlayer implements RingtonePlayer {
+ private final IBinder mRemoteToken = new Binder();
+ private final IRingtonePlayer mRemoteRingtoneService;
+ private final Uri mCanonicalUri;
+ private final int mEnabledMedia;
+ private final VibrationEffect mVibrationEffect;
+ private final VolumeShaper.Configuration mVolumeShaperConfig;
+ private final AudioAttributes mAudioAttributes;
+ private final boolean mUseExactAudioAttributes;
+ private boolean mIsLooping;
+ private float mVolume;
+ private boolean mHapticGeneratorEnabled;
+
+ RemoteRingtonePlayer(@NonNull IRingtonePlayer remoteRingtoneService,
+ @NonNull Uri uri, @NonNull AudioAttributes audioAttributes,
+ boolean useExactAudioAttributes,
+ @RingtoneMedia int enabledMedia, @Nullable VibrationEffect vibrationEffect,
+ @Nullable VolumeShaper.Configuration volumeShaperConfig,
+ boolean hapticGeneratorEnabled, boolean initialIsLooping, float initialVolume) {
+ mRemoteRingtoneService = remoteRingtoneService;
+ mCanonicalUri = (uri == null) ? null : uri.getCanonicalUri();
+ mAudioAttributes = audioAttributes;
+ mUseExactAudioAttributes = useExactAudioAttributes;
+ mEnabledMedia = enabledMedia;
+ mVibrationEffect = vibrationEffect;
+ mVolumeShaperConfig = volumeShaperConfig;
+ mHapticGeneratorEnabled = hapticGeneratorEnabled;
+ mIsLooping = initialIsLooping;
+ mVolume = initialVolume;
+ }
+
+ @Override
+ public boolean play() {
+ try {
+ mRemoteRingtoneService.playRemoteRingtone(mRemoteToken, mCanonicalUri,
+ mAudioAttributes, mUseExactAudioAttributes, mEnabledMedia, mVibrationEffect,
+ mVolume, mIsLooping, mHapticGeneratorEnabled, mVolumeShaperConfig);
+ return true;
+ } catch (RemoteException e) {
+ Log.w(TAG, "Problem playing ringtone: " + e);
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isPlaying() {
+ try {
+ return mRemoteRingtoneService.isPlaying(mRemoteToken);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Problem checking ringtone isPlaying: " + e);
+ return false;
+ }
+ }
+
+ @Override
+ public void stopAndRelease() {
+ try {
+ mRemoteRingtoneService.stop(mRemoteToken);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Problem stopping ringtone: " + e);
+ }
+ }
+
+ @Override
+ public void setPreferredDevice(@Nullable AudioDeviceInfo audioDeviceInfo) {
+ // un-implemented for remote (but not used outside system).
+ }
+
+ @Override
+ public void setLooping(boolean looping) {
+ mIsLooping = looping;
+ try {
+ mRemoteRingtoneService.setLooping(mRemoteToken, looping);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Problem setting looping: " + e);
+ }
+ }
+
+ @Override
+ public void setHapticGeneratorEnabled(boolean enabled) {
+ mHapticGeneratorEnabled = enabled;
+ try {
+ mRemoteRingtoneService.setHapticGeneratorEnabled(mRemoteToken, enabled);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Problem setting hapticGeneratorEnabled: " + e);
+ }
+ }
+
+ @Override
+ public void setVolume(float volume) {
+ mVolume = volume;
+ try {
+ mRemoteRingtoneService.setVolume(mRemoteToken, volume);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Problem setting volume: " + e);
+ }
+ }
+
+ @Override
+ public boolean hasHapticChannels() {
+ // FIXME: support remote player, or internalize haptic channels support and remove
+ // entirely.
+ return false;
+ }
+ }
+
+ /**
* Interface for intercepting static methods and constructors, for unit testing only.
* @hide
*/
@@ -735,47 +1071,4 @@ public class Ringtone {
}
}
-
- /**
- * Interface for alternative Ringtone implementations. See the public Ringtone methods that
- * delegate to these for documentation.
- * @hide
- */
- interface ApiInterface {
- void setStreamType(int streamType);
- int getStreamType();
- void setAudioAttributes(AudioAttributes attributes);
- boolean getPreferBuiltinDevice();
- VolumeShaper.Configuration getVolumeShaperConfig();
- boolean isLocalOnly();
- boolean isUsingRemotePlayer();
- boolean reinitializeActivePlayer();
- boolean hasHapticChannels();
- AudioAttributes getAudioAttributes();
- void setLooping(boolean looping);
- boolean isLooping();
- void setVolume(float volume);
- float getVolume();
- boolean setHapticGeneratorEnabled(boolean enabled);
- boolean isHapticGeneratorEnabled();
- String getTitle(Context context);
- Uri getUri();
- void play();
- void stop();
- boolean isPlaying();
- // V2 future-public methods
- @RingtoneMedia int getEnabledMedia();
- VibrationEffect getVibrationEffect();
- }
-
- /**
- * Switch for using the new ringtone implementation (RingtoneV1 vs RingtoneV2). This may be
- * called from both system server and app-side sdk.
- *
- * @hide
- */
- public static boolean useRingtoneV2() {
- // TODO(b/293846645): chang eto new flagging infra
- return SystemProperties.getBoolean("persist.audio.ringtone.use_v2", false);
- }
}
diff --git a/media/java/android/media/RingtoneManager.java b/media/java/android/media/RingtoneManager.java
index d91c45acef3a..db0970ff7533 100644
--- a/media/java/android/media/RingtoneManager.java
+++ b/media/java/android/media/RingtoneManager.java
@@ -497,32 +497,13 @@ public class RingtoneManager {
mPreviousRingtone.stop();
}
- Ringtone ringtone;
- Uri positionUri = getRingtoneUri(position);
- if (Ringtone.useRingtoneV2()) {
- mPreviousRingtone = new Ringtone.Builder(
- mContext, Ringtone.MEDIA_SOUND, getDefaultAudioAttributes(mType))
- .setUri(positionUri)
- .build();
- } else {
- mPreviousRingtone = createRingtoneV1WithStreamType(mContext, positionUri,
- inferStreamType(), /* volumeShaperConfig= */ null);
- }
+ mPreviousRingtone = new Ringtone.Builder(
+ mContext, Ringtone.MEDIA_SOUND, getDefaultAudioAttributes(mType))
+ .setUri(getRingtoneUri(position))
+ .build();
return mPreviousRingtone;
}
- private static Ringtone createRingtoneV1WithStreamType(
- final Context context, Uri ringtoneUri, int streamType,
- @Nullable VolumeShaper.Configuration volumeShaperConfig) {
- try {
- return Ringtone.createV1WithCustomStreamType(context, streamType, ringtoneUri,
- volumeShaperConfig);
- } catch (Exception ex) {
- Log.e(TAG, "Failed to open ringtone " + ringtoneUri + ": " + ex);
- }
- return null;
- }
-
/**
* Gets a {@link Uri} for the ringtone at the given position in the {@link Cursor}.
*
@@ -803,14 +784,9 @@ public class RingtoneManager {
* @return A {@link Ringtone} for the given URI, or null.
*/
public static Ringtone getRingtone(final Context context, Uri ringtoneUri) {
- if (Ringtone.useRingtoneV2()) {
- return new Ringtone.Builder(
- context, Ringtone.MEDIA_SOUND, getDefaultAudioAttributes(-1))
- .setUri(ringtoneUri)
- .build();
- } else {
- return createRingtoneV1WithStreamType(context, ringtoneUri, -1, null);
- }
+ return new Ringtone.Builder(context, Ringtone.MEDIA_SOUND, getDefaultAudioAttributes(-1))
+ .setUri(ringtoneUri)
+ .build();
}
/**
@@ -820,22 +796,11 @@ public class RingtoneManager {
@Nullable VolumeShaper.Configuration volumeShaperConfig,
AudioAttributes audioAttributes) {
// TODO: move caller(s) away from this method: inline the builder call.
- if (Ringtone.useRingtoneV2()) {
- return new Ringtone.Builder(context, Ringtone.MEDIA_SOUND, audioAttributes)
- .setUri(ringtoneUri)
- .setVolumeShaperConfig(volumeShaperConfig)
- .setUseExactAudioAttributes(true) // May be using audio-coupled via attrs
- .build();
- } else {
- try {
- return Ringtone.createV1WithCustomAudioAttributes(context, audioAttributes,
- ringtoneUri, volumeShaperConfig, /* allowRemote= */ true);
- } catch (Exception ex) {
- // Match broad catching of createRingtoneV1.
- Log.e(TAG, "Failed to open ringtone " + ringtoneUri + ": " + ex);
- return null;
- }
- }
+ return new Ringtone.Builder(context, Ringtone.MEDIA_SOUND, audioAttributes)
+ .setUri(ringtoneUri)
+ .setVolumeShaperConfig(volumeShaperConfig)
+ .setUseExactAudioAttributes(true) // May be using audio-coupled via attrs
+ .build();
}
/**
diff --git a/media/java/android/media/RingtoneV1.java b/media/java/android/media/RingtoneV1.java
deleted file mode 100644
index 3c54d4a0d166..000000000000
--- a/media/java/android/media/RingtoneV1.java
+++ /dev/null
@@ -1,614 +0,0 @@
-/*
- * Copyright (C) 2023 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.Nullable;
-import android.compat.annotation.UnsupportedAppUsage;
-import android.content.Context;
-import android.content.res.AssetFileDescriptor;
-import android.content.res.Resources.NotFoundException;
-import android.media.audiofx.HapticGenerator;
-import android.net.Uri;
-import android.os.Binder;
-import android.os.Build;
-import android.os.RemoteException;
-import android.os.Trace;
-import android.os.VibrationEffect;
-import android.provider.MediaStore;
-import android.provider.MediaStore.MediaColumns;
-import android.util.Log;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.io.IOException;
-import java.util.ArrayList;
-
-/**
- * Hosts original Ringtone implementation, retained for flagging large builder+vibration features
- * in RingtoneV2.java. This does not support new features in the V2 builder.
- *
- * Only modified methods are moved here.
- *
- * @hide
- */
-class RingtoneV1 implements Ringtone.ApiInterface {
- private static final String TAG = "RingtoneV1";
- private static final boolean LOGD = true;
-
- private static final String[] MEDIA_COLUMNS = new String[] {
- MediaStore.Audio.Media._ID,
- MediaStore.Audio.Media.TITLE
- };
- /** Selection that limits query results to just audio files */
- private static final String MEDIA_SELECTION = MediaColumns.MIME_TYPE + " LIKE 'audio/%' OR "
- + MediaColumns.MIME_TYPE + " IN ('application/ogg', 'application/x-flac')";
-
- // keep references on active Ringtones until stopped or completion listener called.
- private static final ArrayList<RingtoneV1> sActiveRingtones = new ArrayList<>();
-
- private final Context mContext;
- private final AudioManager mAudioManager;
- private VolumeShaper.Configuration mVolumeShaperConfig;
- private VolumeShaper mVolumeShaper;
-
- /**
- * Flag indicating if we're allowed to fall back to remote playback using
- * {@link #mRemotePlayer}. Typically this is false when we're the remote
- * player and there is nobody else to delegate to.
- */
- private final boolean mAllowRemote;
- private final IRingtonePlayer mRemotePlayer;
- private final Binder mRemoteToken;
-
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- private MediaPlayer mLocalPlayer;
- private final MyOnCompletionListener mCompletionListener = new MyOnCompletionListener();
- private HapticGenerator mHapticGenerator;
-
- @UnsupportedAppUsage
- private Uri mUri;
- private String mTitle;
-
- private AudioAttributes mAudioAttributes = new AudioAttributes.Builder()
- .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE)
- .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
- .build();
- private boolean mPreferBuiltinDevice;
- // playback properties, use synchronized with mPlaybackSettingsLock
- private boolean mIsLooping = false;
- private float mVolume = 1.0f;
- private boolean mHapticGeneratorEnabled = false;
- private final Object mPlaybackSettingsLock = new Object();
-
- /** {@hide} */
- @UnsupportedAppUsage
- public RingtoneV1(Context context, boolean allowRemote) {
- mContext = context;
- mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
- mAllowRemote = allowRemote;
- mRemotePlayer = allowRemote ? mAudioManager.getRingtonePlayer() : null;
- mRemoteToken = allowRemote ? new Binder() : null;
- }
-
- /**
- * Sets the stream type where this ringtone will be played.
- *
- * @param streamType The stream, see {@link AudioManager}.
- * @deprecated use {@link #setAudioAttributes(AudioAttributes)}
- */
- @Deprecated
- public void setStreamType(int streamType) {
- PlayerBase.deprecateStreamTypeForPlayback(streamType, "Ringtone", "setStreamType()");
- setAudioAttributes(new AudioAttributes.Builder()
- .setInternalLegacyStreamType(streamType)
- .build());
- }
-
- /**
- * Gets the stream type where this ringtone will be played.
- *
- * @return The stream type, see {@link AudioManager}.
- * @deprecated use of stream types is deprecated, see
- * {@link #setAudioAttributes(AudioAttributes)}
- */
- @Deprecated
- public int getStreamType() {
- return AudioAttributes.toLegacyStreamType(mAudioAttributes);
- }
-
- /**
- * Sets the {@link AudioAttributes} for this ringtone.
- * @param attributes the non-null attributes characterizing this ringtone.
- */
- public void setAudioAttributes(AudioAttributes attributes)
- throws IllegalArgumentException {
- setAudioAttributesField(attributes);
- // The audio attributes have to be set before the media player is prepared.
- // Re-initialize it.
- setUri(mUri, mVolumeShaperConfig);
- reinitializeActivePlayer();
- }
-
- /**
- * Same as {@link #setAudioAttributes(AudioAttributes)} except this one does not create
- * the media player.
- * @hide
- */
- public void setAudioAttributesField(@Nullable AudioAttributes attributes) {
- if (attributes == null) {
- throw new IllegalArgumentException("Invalid null AudioAttributes for Ringtone");
- }
- mAudioAttributes = attributes;
- }
-
- /**
- * Finds the output device of type {@link AudioDeviceInfo#TYPE_BUILTIN_SPEAKER}. This device is
- * the one on which outgoing audio for SIM calls is played.
- *
- * @param audioManager the audio manage.
- * @return the {@link AudioDeviceInfo} corresponding to the builtin device, or {@code null} if
- * none can be found.
- */
- private AudioDeviceInfo getBuiltinDevice(AudioManager audioManager) {
- AudioDeviceInfo[] deviceList = audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS);
- for (AudioDeviceInfo device : deviceList) {
- if (device.getType() == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER) {
- return device;
- }
- }
- return null;
- }
-
- /**
- * Sets the preferred device of the ringtong playback to the built-in device.
- *
- * @hide
- */
- public boolean preferBuiltinDevice(boolean enable) {
- mPreferBuiltinDevice = enable;
- if (mLocalPlayer == null) {
- return true;
- }
- return mLocalPlayer.setPreferredDevice(getBuiltinDevice(mAudioManager));
- }
-
- /**
- * Creates a local media player for the ringtone using currently set attributes.
- * @return true if media player creation succeeded or is deferred,
- * false if it did not succeed and can't be tried remotely.
- * @hide
- */
- public boolean reinitializeActivePlayer() {
- Trace.beginSection("reinitializeActivePlayer");
- if (mUri == null) {
- Log.e(TAG, "Could not create media player as no URI was provided.");
- return mAllowRemote && mRemotePlayer != null;
- }
- destroyLocalPlayer();
- // try opening uri locally before delegating to remote player
- mLocalPlayer = new MediaPlayer();
- try {
- mLocalPlayer.setDataSource(mContext, mUri);
- mLocalPlayer.setAudioAttributes(mAudioAttributes);
- mLocalPlayer.setPreferredDevice(
- mPreferBuiltinDevice ? getBuiltinDevice(mAudioManager) : null);
- synchronized (mPlaybackSettingsLock) {
- applyPlaybackProperties_sync();
- }
- if (mVolumeShaperConfig != null) {
- mVolumeShaper = mLocalPlayer.createVolumeShaper(mVolumeShaperConfig);
- }
- mLocalPlayer.prepare();
-
- } catch (SecurityException | IOException e) {
- destroyLocalPlayer();
- if (!mAllowRemote) {
- Log.w(TAG, "Remote playback not allowed: " + e);
- }
- }
-
- if (LOGD) {
- if (mLocalPlayer != null) {
- Log.d(TAG, "Successfully created local player");
- } else {
- Log.d(TAG, "Problem opening; delegating to remote player");
- }
- }
- Trace.endSection();
- return mLocalPlayer != null || (mAllowRemote && mRemotePlayer != null);
- }
-
- /**
- * Same as AudioManager.hasHapticChannels except it assumes an already created ringtone.
- * If the ringtone has not been created, it will load based on URI provided at {@link #setUri}
- * and if not URI has been set, it will assume no haptic channels are present.
- * @hide
- */
- public boolean hasHapticChannels() {
- // FIXME: support remote player, or internalize haptic channels support and remove entirely.
- try {
- android.os.Trace.beginSection("Ringtone.hasHapticChannels");
- if (mLocalPlayer != null) {
- for(MediaPlayer.TrackInfo trackInfo : mLocalPlayer.getTrackInfo()) {
- if (trackInfo.hasHapticChannels()) {
- return true;
- }
- }
- }
- } finally {
- android.os.Trace.endSection();
- }
- return false;
- }
-
- /**
- * Returns whether a local player has been created for this ringtone.
- * @hide
- */
- @VisibleForTesting
- public boolean hasLocalPlayer() {
- return mLocalPlayer != null;
- }
-
- public @Ringtone.RingtoneMedia int getEnabledMedia() {
- return Ringtone.MEDIA_SOUND; // RingtoneV2 only
- }
-
- public VibrationEffect getVibrationEffect() {
- return null; // RingtoneV2 only
- }
-
- /**
- * Returns the {@link AudioAttributes} used by this object.
- * @return the {@link AudioAttributes} that were set with
- * {@link #setAudioAttributes(AudioAttributes)} or the default attributes if none were set.
- */
- public AudioAttributes getAudioAttributes() {
- return mAudioAttributes;
- }
-
- /**
- * Sets the player to be looping or non-looping.
- * @param looping whether to loop or not.
- */
- public void setLooping(boolean looping) {
- synchronized (mPlaybackSettingsLock) {
- mIsLooping = looping;
- applyPlaybackProperties_sync();
- }
- }
-
- /**
- * Returns whether the looping mode was enabled on this player.
- * @return true if this player loops when playing.
- */
- public boolean isLooping() {
- synchronized (mPlaybackSettingsLock) {
- return mIsLooping;
- }
- }
-
- /**
- * Sets the volume on this player.
- * @param volume a raw scalar in range 0.0 to 1.0, where 0.0 mutes this player, and 1.0
- * corresponds to no attenuation being applied.
- */
- public void setVolume(float volume) {
- synchronized (mPlaybackSettingsLock) {
- if (volume < 0.0f) { volume = 0.0f; }
- if (volume > 1.0f) { volume = 1.0f; }
- mVolume = volume;
- applyPlaybackProperties_sync();
- }
- }
-
- /**
- * Returns the volume scalar set on this player.
- * @return a value between 0.0f and 1.0f.
- */
- public float getVolume() {
- synchronized (mPlaybackSettingsLock) {
- return mVolume;
- }
- }
-
- /**
- * Enable or disable the {@link android.media.audiofx.HapticGenerator} effect. The effect can
- * only be enabled on devices that support the effect.
- *
- * @return true if the HapticGenerator effect is successfully enabled. Otherwise, return false.
- * @see android.media.audiofx.HapticGenerator#isAvailable()
- */
- public boolean setHapticGeneratorEnabled(boolean enabled) {
- if (!HapticGenerator.isAvailable()) {
- return false;
- }
- synchronized (mPlaybackSettingsLock) {
- mHapticGeneratorEnabled = enabled;
- applyPlaybackProperties_sync();
- }
- return true;
- }
-
- /**
- * Return whether the {@link android.media.audiofx.HapticGenerator} effect is enabled or not.
- * @return true if the HapticGenerator is enabled.
- */
- public boolean isHapticGeneratorEnabled() {
- synchronized (mPlaybackSettingsLock) {
- return mHapticGeneratorEnabled;
- }
- }
-
- /**
- * Must be called synchronized on mPlaybackSettingsLock
- */
- private void applyPlaybackProperties_sync() {
- if (mLocalPlayer != null) {
- mLocalPlayer.setVolume(mVolume);
- mLocalPlayer.setLooping(mIsLooping);
- if (mHapticGenerator == null && mHapticGeneratorEnabled) {
- mHapticGenerator = HapticGenerator.create(mLocalPlayer.getAudioSessionId());
- }
- if (mHapticGenerator != null) {
- mHapticGenerator.setEnabled(mHapticGeneratorEnabled);
- }
- } else if (mAllowRemote && (mRemotePlayer != null)) {
- try {
- mRemotePlayer.setPlaybackProperties(
- mRemoteToken, mVolume, mIsLooping, mHapticGeneratorEnabled);
- } catch (RemoteException e) {
- Log.w(TAG, "Problem setting playback properties: ", e);
- }
- } else {
- Log.w(TAG,
- "Neither local nor remote player available when applying playback properties");
- }
- }
-
- /**
- * Returns a human-presentable title for ringtone. Looks in media
- * content provider. If not in either, uses the filename
- *
- * @param context A context used for querying.
- */
- public String getTitle(Context context) {
- if (mTitle != null) return mTitle;
- return mTitle = Ringtone.getTitle(context, mUri, true /*followSettingsUri*/, mAllowRemote);
- }
-
- /**
- * Set {@link Uri} to be used for ringtone playback.
- * {@link IRingtonePlayer}.
- *
- * @hide
- */
- @UnsupportedAppUsage
- public void setUri(Uri uri) {
- setUri(uri, null);
- }
-
- /**
- * @hide
- */
- public void setVolumeShaperConfig(@Nullable VolumeShaper.Configuration volumeShaperConfig) {
- mVolumeShaperConfig = volumeShaperConfig;
- }
-
- /**
- * Set {@link Uri} to be used for ringtone playback. Attempts to open
- * locally, otherwise will delegate playback to remote
- * {@link IRingtonePlayer}. Add {@link VolumeShaper} if required.
- *
- * @hide
- */
- public void setUri(Uri uri, @Nullable VolumeShaper.Configuration volumeShaperConfig) {
- mVolumeShaperConfig = volumeShaperConfig;
- mUri = uri;
- if (mUri == null) {
- destroyLocalPlayer();
- }
- }
-
- /** {@hide} */
- @UnsupportedAppUsage
- public Uri getUri() {
- return mUri;
- }
-
- /**
- * Plays the ringtone.
- */
- public void play() {
- if (mLocalPlayer != null) {
- // Play ringtones if stream volume is over 0 or if it is a haptic-only ringtone
- // (typically because ringer mode is vibrate).
- if (mAudioManager.getStreamVolume(AudioAttributes.toLegacyStreamType(mAudioAttributes))
- != 0) {
- startLocalPlayer();
- } else if (!mAudioAttributes.areHapticChannelsMuted() && hasHapticChannels()) {
- // is haptic only ringtone
- startLocalPlayer();
- }
- } else if (mAllowRemote && (mRemotePlayer != null) && (mUri != null)) {
- final Uri canonicalUri = mUri.getCanonicalUri();
- final boolean looping;
- final float volume;
- synchronized (mPlaybackSettingsLock) {
- looping = mIsLooping;
- volume = mVolume;
- }
- try {
- mRemotePlayer.playWithVolumeShaping(mRemoteToken, canonicalUri, mAudioAttributes,
- volume, looping, mVolumeShaperConfig);
- } catch (RemoteException e) {
- if (!playFallbackRingtone()) {
- Log.w(TAG, "Problem playing ringtone: " + e);
- }
- }
- } else {
- if (!playFallbackRingtone()) {
- Log.w(TAG, "Neither local nor remote playback available");
- }
- }
- }
-
- /**
- * Stops a playing ringtone.
- */
- public void stop() {
- if (mLocalPlayer != null) {
- destroyLocalPlayer();
- } else if (mAllowRemote && (mRemotePlayer != null)) {
- try {
- mRemotePlayer.stop(mRemoteToken);
- } catch (RemoteException e) {
- Log.w(TAG, "Problem stopping ringtone: " + e);
- }
- }
- }
-
- private void destroyLocalPlayer() {
- if (mLocalPlayer != null) {
- if (mHapticGenerator != null) {
- mHapticGenerator.release();
- mHapticGenerator = null;
- }
- mLocalPlayer.setOnCompletionListener(null);
- mLocalPlayer.reset();
- mLocalPlayer.release();
- mLocalPlayer = null;
- mVolumeShaper = null;
- synchronized (sActiveRingtones) {
- sActiveRingtones.remove(this);
- }
- }
- }
-
- private void startLocalPlayer() {
- if (mLocalPlayer == null) {
- return;
- }
- synchronized (sActiveRingtones) {
- sActiveRingtones.add(this);
- }
- if (LOGD) {
- Log.d(TAG, "Starting ringtone playback");
- }
- mLocalPlayer.setOnCompletionListener(mCompletionListener);
- mLocalPlayer.start();
- if (mVolumeShaper != null) {
- mVolumeShaper.apply(VolumeShaper.Operation.PLAY);
- }
- }
-
- /**
- * Whether this ringtone is currently playing.
- *
- * @return True if playing, false otherwise.
- */
- public boolean isPlaying() {
- if (mLocalPlayer != null) {
- return mLocalPlayer.isPlaying();
- } else if (mAllowRemote && (mRemotePlayer != null)) {
- try {
- return mRemotePlayer.isPlaying(mRemoteToken);
- } catch (RemoteException e) {
- Log.w(TAG, "Problem checking ringtone: " + e);
- return false;
- }
- } else {
- Log.w(TAG, "Neither local nor remote playback available");
- return false;
- }
- }
-
- private boolean playFallbackRingtone() {
- int streamType = AudioAttributes.toLegacyStreamType(mAudioAttributes);
- if (mAudioManager.getStreamVolume(streamType) == 0) {
- return false;
- }
- int ringtoneType = RingtoneManager.getDefaultType(mUri);
- if (ringtoneType != -1 &&
- RingtoneManager.getActualDefaultRingtoneUri(mContext, ringtoneType) == null) {
- Log.w(TAG, "not playing fallback for " + mUri);
- return false;
- }
- // Default ringtone, try fallback ringtone.
- try {
- AssetFileDescriptor afd = mContext.getResources().openRawResourceFd(
- com.android.internal.R.raw.fallbackring);
- if (afd == null) {
- Log.e(TAG, "Could not load fallback ringtone");
- return false;
- }
- mLocalPlayer = new MediaPlayer();
- if (afd.getDeclaredLength() < 0) {
- mLocalPlayer.setDataSource(afd.getFileDescriptor());
- } else {
- mLocalPlayer.setDataSource(afd.getFileDescriptor(),
- afd.getStartOffset(),
- afd.getDeclaredLength());
- }
- mLocalPlayer.setAudioAttributes(mAudioAttributes);
- synchronized (mPlaybackSettingsLock) {
- applyPlaybackProperties_sync();
- }
- if (mVolumeShaperConfig != null) {
- mVolumeShaper = mLocalPlayer.createVolumeShaper(mVolumeShaperConfig);
- }
- mLocalPlayer.prepare();
- startLocalPlayer();
- afd.close();
- } catch (IOException ioe) {
- destroyLocalPlayer();
- Log.e(TAG, "Failed to open fallback ringtone");
- return false;
- } catch (NotFoundException nfe) {
- Log.e(TAG, "Fallback ringtone does not exist");
- return false;
- }
- return true;
- }
-
- public boolean getPreferBuiltinDevice() {
- return mPreferBuiltinDevice;
- }
-
- public VolumeShaper.Configuration getVolumeShaperConfig() {
- return mVolumeShaperConfig;
- }
-
- public boolean isLocalOnly() {
- return mAllowRemote;
- }
-
- public boolean isUsingRemotePlayer() {
- // V2 testing api, but this is the v1 approximation.
- return (mLocalPlayer == null) && mAllowRemote && (mRemotePlayer != null);
- }
-
- class MyOnCompletionListener implements MediaPlayer.OnCompletionListener {
- @Override
- public void onCompletion(MediaPlayer mp) {
- synchronized (sActiveRingtones) {
- sActiveRingtones.remove(RingtoneV1.this);
- }
- mp.setOnCompletionListener(null); // Help the Java GC: break the refcount cycle.
- }
- }
-}
diff --git a/media/java/android/media/RingtoneV2.java b/media/java/android/media/RingtoneV2.java
deleted file mode 100644
index f1a81553bdfc..000000000000
--- a/media/java/android/media/RingtoneV2.java
+++ /dev/null
@@ -1,690 +0,0 @@
-/*
- * Copyright (C) 2023 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.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.compat.annotation.UnsupportedAppUsage;
-import android.content.Context;
-import android.content.res.AssetFileDescriptor;
-import android.content.res.Resources.NotFoundException;
-import android.media.Ringtone.Injectables;
-import android.net.Uri;
-import android.os.Binder;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.Trace;
-import android.os.VibrationEffect;
-import android.os.Vibrator;
-import android.provider.MediaStore;
-import android.provider.MediaStore.MediaColumns;
-import android.util.Log;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.io.IOException;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * New Ringtone implementation, supporting vibration as well as sound, and configuration via a
- * builder. During flagged transition, the original implementation is in RingtoneV1.java.
- *
- * Only modified methods are moved here.
- *
- * @hide
- */
-class RingtoneV2 implements Ringtone.ApiInterface {
- private static final String TAG = "RingtoneV2";
-
- /**
- * The ringtone should only play sound. Any vibration is managed externally.
- * @hide
- */
- public static final int MEDIA_SOUND = 1;
- /**
- * The ringtone should only play vibration. Any sound is managed externally.
- * Requires the {@link android.Manifest.permission#VIBRATE} permission.
- * @hide
- */
- public static final int MEDIA_VIBRATION = 1 << 1;
- /**
- * The ringtone should play sound and vibration.
- * @hide
- */
- public static final int MEDIA_SOUND_AND_VIBRATION = MEDIA_SOUND | MEDIA_VIBRATION;
-
- // This is not a public value, because apps shouldn't enable "all" media - that wouldn't be
- // safe if new media types were added.
- static final int MEDIA_ALL = MEDIA_SOUND | MEDIA_VIBRATION;
-
- /**
- * Declares the types of media that this Ringtone is allowed to play.
- * @hide
- */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(prefix = "MEDIA_", value = {
- MEDIA_SOUND,
- MEDIA_VIBRATION,
- MEDIA_SOUND_AND_VIBRATION,
- })
- public @interface RingtoneMedia {}
-
- private static final String[] MEDIA_COLUMNS = new String[] {
- MediaStore.Audio.Media._ID,
- MediaStore.Audio.Media.TITLE
- };
- /** Selection that limits query results to just audio files */
- private static final String MEDIA_SELECTION = MediaColumns.MIME_TYPE + " LIKE 'audio/%' OR "
- + MediaColumns.MIME_TYPE + " IN ('application/ogg', 'application/x-flac')";
-
- private final Context mContext;
- private final Vibrator mVibrator;
- private final AudioManager mAudioManager;
- private VolumeShaper.Configuration mVolumeShaperConfig;
-
- /**
- * Flag indicating if we're allowed to fall back to remote playback using
- * {@link #mRemoteRingtoneService}. Typically this is false when we're the remote
- * player and there is nobody else to delegate to.
- */
- private final boolean mAllowRemote;
- private final IRingtonePlayer mRemoteRingtoneService;
- private final Injectables mInjectables;
-
- private final int mEnabledMedia;
-
- private final Uri mUri;
- private String mTitle;
-
- private AudioAttributes mAudioAttributes;
- private boolean mUseExactAudioAttributes;
- private boolean mPreferBuiltinDevice;
- private RingtonePlayer mActivePlayer;
- // playback properties, use synchronized with mPlaybackSettingsLock
- private boolean mIsLooping;
- private float mVolume;
- private boolean mHapticGeneratorEnabled;
- private final Object mPlaybackSettingsLock = new Object();
- private final VibrationEffect mVibrationEffect;
-
- /** Only for use by Ringtone constructor */
- RingtoneV2(@NonNull Context context, @NonNull Injectables injectables,
- boolean allowRemote, @Ringtone.RingtoneMedia int enabledMedia,
- @Nullable Uri uri, @NonNull AudioAttributes audioAttributes,
- boolean useExactAudioAttributes,
- @Nullable VolumeShaper.Configuration volumeShaperConfig,
- boolean preferBuiltinDevice, float soundVolume, boolean looping,
- boolean hapticGeneratorEnabled, @Nullable VibrationEffect vibrationEffect) {
- // Context
- mContext = context;
- mInjectables = injectables;
- mVibrator = mContext.getSystemService(Vibrator.class);
- mAudioManager = mContext.getSystemService(AudioManager.class);
- mRemoteRingtoneService = allowRemote ? mAudioManager.getRingtonePlayer() : null;
- mAllowRemote = (mRemoteRingtoneService != null); // Only set if allowed, and present.
-
- // Properties potentially propagated to remote player.
- mEnabledMedia = enabledMedia;
- mUri = uri;
- mAudioAttributes = audioAttributes;
- mUseExactAudioAttributes = useExactAudioAttributes;
- mVolumeShaperConfig = volumeShaperConfig;
- mPreferBuiltinDevice = preferBuiltinDevice; // system-only, not supported for remote play.
- mVolume = soundVolume;
- mIsLooping = looping;
- mHapticGeneratorEnabled = hapticGeneratorEnabled;
- mVibrationEffect = vibrationEffect;
- }
-
- /** @hide */
- @RingtoneMedia
- public int getEnabledMedia() {
- return mEnabledMedia;
- }
-
- /**
- * Sets the stream type where this ringtone will be played.
- *
- * @param streamType The stream, see {@link AudioManager}.
- * @deprecated use {@link #setAudioAttributes(AudioAttributes)}
- */
- @Deprecated
- public void setStreamType(int streamType) {
- setAudioAttributes(
- getAudioAttributesForLegacyStreamType(streamType, "setStreamType()"));
- }
-
- private AudioAttributes getAudioAttributesForLegacyStreamType(int streamType, String originOp) {
- PlayerBase.deprecateStreamTypeForPlayback(streamType, "Ringtone", originOp);
- return new AudioAttributes.Builder()
- .setInternalLegacyStreamType(streamType)
- .build();
- }
-
- /**
- * Gets the stream type where this ringtone will be played.
- *
- * @return The stream type, see {@link AudioManager}.
- * @deprecated use of stream types is deprecated, see
- * {@link #setAudioAttributes(AudioAttributes)}
- */
- @Deprecated
- public int getStreamType() {
- return AudioAttributes.toLegacyStreamType(mAudioAttributes);
- }
-
- /**
- * Sets the {@link AudioAttributes} for this ringtone.
- * @param attributes the non-null attributes characterizing this ringtone.
- */
- public void setAudioAttributes(AudioAttributes attributes)
- throws IllegalArgumentException {
- // TODO: deprecate this method - it will be done with a builder.
- if (attributes == null) {
- throw new IllegalArgumentException("Invalid null AudioAttributes for Ringtone");
- }
- mAudioAttributes = attributes;
- // Setting the audio attributes requires re-initializing the player.
- if (mActivePlayer != null) {
- // The audio attributes have to be set before the media player is prepared.
- // Re-initialize it.
- reinitializeActivePlayer();
- }
- }
-
- /**
- * Returns the vibration effect that this ringtone was created with, if vibration is enabled.
- * Otherwise, returns null.
- * @hide
- */
- @Nullable
- public VibrationEffect getVibrationEffect() {
- return mVibrationEffect;
- }
-
- /** @hide */
- @VisibleForTesting
- public boolean getPreferBuiltinDevice() {
- return mPreferBuiltinDevice;
- }
-
- /** @hide */
- @VisibleForTesting
- public VolumeShaper.Configuration getVolumeShaperConfig() {
- return mVolumeShaperConfig;
- }
-
- /**
- * Returns whether this player is local only, or can defer to the remote player. The
- * result may differ from the builder if there is no remote player available at all.
- * @hide
- */
- @VisibleForTesting
- public boolean isLocalOnly() {
- return !mAllowRemote;
- }
-
- /** @hide */
- @VisibleForTesting
- public boolean isUsingRemotePlayer() {
- return mActivePlayer instanceof RemoteRingtonePlayer;
- }
-
- /**
- * Finds the output device of type {@link AudioDeviceInfo#TYPE_BUILTIN_SPEAKER}. This device is
- * the one on which outgoing audio for SIM calls is played.
- *
- * @param audioManager the audio manage.
- * @return the {@link AudioDeviceInfo} corresponding to the builtin device, or {@code null} if
- * none can be found.
- */
- private AudioDeviceInfo getBuiltinDevice(AudioManager audioManager) {
- AudioDeviceInfo[] deviceList = audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS);
- for (AudioDeviceInfo device : deviceList) {
- if (device.getType() == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER) {
- return device;
- }
- }
- return null;
- }
-
- /**
- * Creates a local media player for the ringtone using currently set attributes.
- * @return true if media player creation succeeded or is deferred,
- * false if it did not succeed and can't be tried remotely.
- * @hide
- */
- public boolean reinitializeActivePlayer() {
- // Try creating a local media player, or fallback to creating a remote one.
- Trace.beginSection("reinitializeActivePlayer");
- try {
- if (mActivePlayer != null) {
- // This would only happen if calling the deprecated setAudioAttributes after
- // building the Ringtone.
- stopAndReleaseActivePlayer();
- }
-
- boolean vibrationOnly = (mEnabledMedia & MEDIA_ALL) == MEDIA_VIBRATION;
- // Vibration can come from the audio file if using haptic generator or if haptic
- // channels are a possibility.
- boolean maybeAudioVibration = mUri != null && mInjectables.isHapticPlaybackSupported()
- && (mHapticGeneratorEnabled || !mAudioAttributes.areHapticChannelsMuted());
-
- // VibrationEffect only, use the simplified player without checking for haptic channels.
- if (vibrationOnly && !maybeAudioVibration && mVibrationEffect != null) {
- mActivePlayer = new LocalRingtonePlayer.VibrationEffectPlayer(
- mVibrationEffect, mAudioAttributes, mVibrator, mIsLooping);
- return true;
- }
-
- AudioDeviceInfo preferredDevice =
- mPreferBuiltinDevice ? getBuiltinDevice(mAudioManager) : null;
- if (mUri != null) {
- mActivePlayer = LocalRingtonePlayer.create(mContext, mAudioManager, mVibrator, mUri,
- mAudioAttributes, vibrationOnly, mVibrationEffect, mInjectables,
- mVolumeShaperConfig, preferredDevice, mHapticGeneratorEnabled, mIsLooping,
- mVolume);
- } else {
- // Using the remote player won't help play a null Uri. Revert straight to fallback.
- // The vibration-only case was already covered above.
- mActivePlayer = createFallbackRingtonePlayer();
- // Fall through to attempting remote fallback play if null.
- }
-
- if (mActivePlayer == null && mAllowRemote) {
- mActivePlayer = new RemoteRingtonePlayer(mRemoteRingtoneService, mUri,
- mAudioAttributes, mUseExactAudioAttributes, mEnabledMedia, mVibrationEffect,
- mVolumeShaperConfig, mHapticGeneratorEnabled, mIsLooping, mVolume);
- }
-
- return mActivePlayer != null;
- } finally {
- if (mActivePlayer != null) {
- Log.d(TAG, "Initialized ringtone player with " + mActivePlayer.getClass());
- } else {
- Log.d(TAG, "Failed to initialize ringtone player");
- }
- Trace.endSection();
- }
- }
-
- @Nullable
- private LocalRingtonePlayer createFallbackRingtonePlayer() {
- int ringtoneType = RingtoneManager.getDefaultType(mUri);
- if (ringtoneType != -1
- && RingtoneManager.getActualDefaultRingtoneUri(mContext, ringtoneType) == null) {
- Log.w(TAG, "not playing fallback for " + mUri);
- return null;
- }
- // Default ringtone, try fallback ringtone.
- try (AssetFileDescriptor afd = mContext.getResources().openRawResourceFd(
- com.android.internal.R.raw.fallbackring)) {
- if (afd == null) {
- Log.e(TAG, "Could not load fallback ringtone");
- return null;
- }
-
- AudioDeviceInfo preferredDevice =
- mPreferBuiltinDevice ? getBuiltinDevice(mAudioManager) : null;
- return LocalRingtonePlayer.createForFallback(mAudioManager, mVibrator, afd,
- mAudioAttributes, mVibrationEffect, mInjectables, mVolumeShaperConfig,
- preferredDevice, mIsLooping, mVolume);
- } catch (NotFoundException nfe) {
- Log.e(TAG, "Fallback ringtone does not exist");
- return null;
- } catch (IOException e) {
- // As with the above messages, not including much information about the
- // failure so as not to expose details of the fallback ringtone resource.
- Log.e(TAG, "Exception reading fallback ringtone");
- return null;
- }
- }
-
- /**
- * Same as AudioManager.hasHapticChannels except it assumes an already created ringtone.
- * @hide
- */
- public boolean hasHapticChannels() {
- return (mActivePlayer == null) ? false : mActivePlayer.hasHapticChannels();
- }
-
- /**
- * Returns the {@link AudioAttributes} used by this object.
- * @return the {@link AudioAttributes} that were set with
- * {@link #setAudioAttributes(AudioAttributes)} or the default attributes if none were set.
- */
- public AudioAttributes getAudioAttributes() {
- return mAudioAttributes;
- }
-
- /**
- * Sets the player to be looping or non-looping.
- * @param looping whether to loop or not.
- */
- public void setLooping(boolean looping) {
- synchronized (mPlaybackSettingsLock) {
- mIsLooping = looping;
- if (mActivePlayer != null) {
- mActivePlayer.setLooping(looping);
- }
- }
- }
-
- /**
- * Returns whether the looping mode was enabled on this player.
- * @return true if this player loops when playing.
- */
- public boolean isLooping() {
- synchronized (mPlaybackSettingsLock) {
- return mIsLooping;
- }
- }
-
- /**
- * Sets the volume on this player.
- * @param volume a raw scalar in range 0.0 to 1.0, where 0.0 mutes this player, and 1.0
- * corresponds to no attenuation being applied.
- */
- public void setVolume(float volume) {
- // Ignore if sound not enabled.
- if ((mEnabledMedia & MEDIA_SOUND) == 0) {
- return;
- }
- if (volume < 0.0f) {
- volume = 0.0f;
- } else if (volume > 1.0f) {
- volume = 1.0f;
- }
-
- synchronized (mPlaybackSettingsLock) {
- mVolume = volume;
- if (mActivePlayer != null) {
- mActivePlayer.setVolume(volume);
- }
- }
- }
-
- /**
- * Returns the volume scalar set on this player.
- * @return a value between 0.0f and 1.0f.
- */
- public float getVolume() {
- synchronized (mPlaybackSettingsLock) {
- return mVolume;
- }
- }
-
- /**
- * Enable or disable the {@link android.media.audiofx.HapticGenerator} effect. The effect can
- * only be enabled on devices that support the effect.
- *
- * @return true if the HapticGenerator effect is successfully enabled. Otherwise, return false.
- * @see android.media.audiofx.HapticGenerator#isAvailable()
- */
- public boolean setHapticGeneratorEnabled(boolean enabled) {
- if (!mInjectables.isHapticGeneratorAvailable()) {
- return false;
- }
- synchronized (mPlaybackSettingsLock) {
- mHapticGeneratorEnabled = enabled;
- if (mActivePlayer != null) {
- mActivePlayer.setHapticGeneratorEnabled(enabled);
- }
- }
- return true;
- }
-
- /**
- * Return whether the {@link android.media.audiofx.HapticGenerator} effect is enabled or not.
- * @return true if the HapticGenerator is enabled.
- */
- public boolean isHapticGeneratorEnabled() {
- synchronized (mPlaybackSettingsLock) {
- return mHapticGeneratorEnabled;
- }
- }
-
- /**
- * Returns a human-presentable title for ringtone. Looks in media
- * content provider. If not in either, uses the filename
- *
- * @param context A context used for querying.
- */
- public String getTitle(Context context) {
- if (mTitle != null) return mTitle;
- return mTitle = Ringtone.getTitle(context, mUri, true /*followSettingsUri*/, mAllowRemote);
- }
-
-
- /** {@hide} */
- @UnsupportedAppUsage
- public Uri getUri() {
- return mUri;
- }
-
- /**
- * Plays the ringtone.
- */
- public void play() {
- if (mActivePlayer != null) {
- Log.d(TAG, "Starting ringtone playback");
- if (mActivePlayer.play()) {
- return;
- } else {
- // Discard active player: play() is only meant to be called once.
- stopAndReleaseActivePlayer();
- }
- }
- if (!playFallbackRingtone()) {
- Log.w(TAG, "Neither local nor remote playback available");
- }
- }
-
- /**
- * Stops a playing ringtone.
- */
- public void stop() {
- stopAndReleaseActivePlayer();
- }
-
- private void stopAndReleaseActivePlayer() {
- if (mActivePlayer != null) {
- mActivePlayer.stopAndRelease();
- mActivePlayer = null;
- }
- }
-
- /**
- * Whether this ringtone is currently playing.
- *
- * @return True if playing, false otherwise.
- */
- public boolean isPlaying() {
- if (mActivePlayer != null) {
- return mActivePlayer.isPlaying();
- } else {
- Log.w(TAG, "No active ringtone player");
- return false;
- }
- }
-
- /**
- * Fallback during the play stage rather than initialization, typically due to an issue
- * communicating with the remote player.
- */
- private boolean playFallbackRingtone() {
- if (mActivePlayer != null) {
- Log.wtf(TAG, "Playing fallback ringtone with another active player");
- stopAndReleaseActivePlayer();
- }
- int streamType = AudioAttributes.toLegacyStreamType(mAudioAttributes);
- if (mAudioManager.getStreamVolume(streamType) == 0) {
- // TODO: Return true? If volume is off, this is a successful play.
- return false;
- }
- mActivePlayer = createFallbackRingtonePlayer();
- if (mActivePlayer == null) {
- return false; // the create method logs if it returns null.
- } else if (mActivePlayer.play()) {
- return true;
- } else {
- stopAndReleaseActivePlayer();
- return false;
- }
- }
-
- void setTitle(String title) {
- mTitle = title;
- }
-
- /**
- * Play a specific ringtone. This interface is implemented by either local (this process) or
- * proxied-remote playback via AudioManager.getRingtonePlayer, so that the caller
- * (Ringtone class) can just use a single player after the initial creation.
- * @hide
- */
- interface RingtonePlayer {
- /**
- * Start playing the ringtone, returning false if there was a problem that
- * requires falling back to the fallback ringtone resource.
- */
- boolean play();
- boolean isPlaying();
- void stopAndRelease();
-
- // Mutating playback methods.
- void setPreferredDevice(@Nullable AudioDeviceInfo audioDeviceInfo);
- void setLooping(boolean looping);
- void setHapticGeneratorEnabled(boolean enabled);
- void setVolume(float volume);
-
- boolean hasHapticChannels();
- }
-
- /**
- * Remote RingtonePlayer. All operations are delegated via the IRingtonePlayer interface, which
- * should ultimately be backed by a RingtoneLocalPlayer within the system services.
- */
- static class RemoteRingtonePlayer implements RingtonePlayer {
- private final IBinder mRemoteToken = new Binder();
- private final IRingtonePlayer mRemoteRingtoneService;
- private final Uri mCanonicalUri;
- private final int mEnabledMedia;
- private final VibrationEffect mVibrationEffect;
- private final VolumeShaper.Configuration mVolumeShaperConfig;
- private final AudioAttributes mAudioAttributes;
- private final boolean mUseExactAudioAttributes;
- private boolean mIsLooping;
- private float mVolume;
- private boolean mHapticGeneratorEnabled;
-
- RemoteRingtonePlayer(@NonNull IRingtonePlayer remoteRingtoneService,
- @NonNull Uri uri, @NonNull AudioAttributes audioAttributes,
- boolean useExactAudioAttributes,
- @RingtoneMedia int enabledMedia, @Nullable VibrationEffect vibrationEffect,
- @Nullable VolumeShaper.Configuration volumeShaperConfig,
- boolean hapticGeneratorEnabled, boolean initialIsLooping, float initialVolume) {
- mRemoteRingtoneService = remoteRingtoneService;
- mCanonicalUri = (uri == null) ? null : uri.getCanonicalUri();
- mAudioAttributes = audioAttributes;
- mUseExactAudioAttributes = useExactAudioAttributes;
- mEnabledMedia = enabledMedia;
- mVibrationEffect = vibrationEffect;
- mVolumeShaperConfig = volumeShaperConfig;
- mHapticGeneratorEnabled = hapticGeneratorEnabled;
- mIsLooping = initialIsLooping;
- mVolume = initialVolume;
- }
-
- @Override
- public boolean play() {
- try {
- mRemoteRingtoneService.playRemoteRingtone(mRemoteToken, mCanonicalUri,
- mAudioAttributes, mUseExactAudioAttributes, mEnabledMedia, mVibrationEffect,
- mVolume, mIsLooping, mHapticGeneratorEnabled, mVolumeShaperConfig);
- return true;
- } catch (RemoteException e) {
- Log.w(TAG, "Problem playing ringtone: " + e);
- return false;
- }
- }
-
- @Override
- public boolean isPlaying() {
- try {
- return mRemoteRingtoneService.isPlaying(mRemoteToken);
- } catch (RemoteException e) {
- Log.w(TAG, "Problem checking ringtone isPlaying: " + e);
- return false;
- }
- }
-
- @Override
- public void stopAndRelease() {
- try {
- mRemoteRingtoneService.stop(mRemoteToken);
- } catch (RemoteException e) {
- Log.w(TAG, "Problem stopping ringtone: " + e);
- }
- }
-
- @Override
- public void setPreferredDevice(@Nullable AudioDeviceInfo audioDeviceInfo) {
- // un-implemented for remote (but not used outside system).
- }
-
- @Override
- public void setLooping(boolean looping) {
- mIsLooping = looping;
- try {
- mRemoteRingtoneService.setLooping(mRemoteToken, looping);
- } catch (RemoteException e) {
- Log.w(TAG, "Problem setting looping: " + e);
- }
- }
-
- @Override
- public void setHapticGeneratorEnabled(boolean enabled) {
- mHapticGeneratorEnabled = enabled;
- try {
- mRemoteRingtoneService.setHapticGeneratorEnabled(mRemoteToken, enabled);
- } catch (RemoteException e) {
- Log.w(TAG, "Problem setting hapticGeneratorEnabled: " + e);
- }
- }
-
- @Override
- public void setVolume(float volume) {
- mVolume = volume;
- try {
- mRemoteRingtoneService.setVolume(mRemoteToken, volume);
- } catch (RemoteException e) {
- Log.w(TAG, "Problem setting volume: " + e);
- }
- }
-
- @Override
- public boolean hasHapticChannels() {
- // FIXME: support remote player, or internalize haptic channels support and remove
- // entirely.
- return false;
- }
- }
-
-}
diff --git a/packages/SoundPicker2/src/com/android/soundpicker/RingtoneFactory.java b/packages/SoundPicker2/src/com/android/soundpicker/RingtoneFactory.java
index 6817f534c00b..cb41eabfa87a 100644
--- a/packages/SoundPicker2/src/com/android/soundpicker/RingtoneFactory.java
+++ b/packages/SoundPicker2/src/com/android/soundpicker/RingtoneFactory.java
@@ -19,7 +19,6 @@ package com.android.soundpicker;
import android.content.Context;
import android.media.AudioAttributes;
import android.media.Ringtone;
-import android.media.RingtoneManager;
import android.net.Uri;
import dagger.hilt.android.qualifiers.ApplicationContext;
@@ -54,7 +53,10 @@ public class RingtoneFactory {
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.setFlags(audioAttributesFlags)
.build();
- return RingtoneManager.getRingtone(mApplicationContext, uri,
- /* volumeShaperConfig= */ null, audioAttributes);
+ // TODO: We are currently only using MEDIA_SOUND for enabledMedia. This will change once we
+ // start playing sound and/or vibration.
+ return new Ringtone.Builder(mApplicationContext, Ringtone.MEDIA_SOUND, audioAttributes)
+ .setUri(uri)
+ .build();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java b/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
index 53d84f6bde65..40ab51d914bd 100644
--- a/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
@@ -57,7 +57,7 @@ import javax.inject.Inject;
@SysUISingleton
public class RingtonePlayer implements CoreStartable {
private static final String TAG = "RingtonePlayer";
- private static final boolean LOGD = true;
+ private static final boolean LOGD = false;
private final Context mContext;
// TODO: support Uri switching under same IBinder
@@ -111,53 +111,9 @@ public class RingtonePlayer implements CoreStartable {
@Override
public void play(IBinder token, Uri uri, AudioAttributes aa, float volume, boolean looping)
throws RemoteException {
- if (Ringtone.useRingtoneV2()) {
- playRemoteRingtone(token, uri, aa, true, Ringtone.MEDIA_SOUND,
- null, volume, looping, /* hapticGenerator= */ false,
- null);
- } else {
- playWithVolumeShaping(token, uri, aa, volume, looping, null);
- }
- }
-
- @Override
- public void playWithVolumeShaping(
- IBinder token, Uri uri, AudioAttributes aa, float volume,
- boolean looping, @Nullable VolumeShaper.Configuration volumeShaperConfig)
- throws RemoteException {
- if (LOGD) {
- Log.d(TAG, "playWithVolumeShaping(token=" + token + ", uri=" + uri + ", uid="
- + Binder.getCallingUid() + ")");
- }
- Client client;
- synchronized (mClients) {
- client = mClients.get(token);
- }
- // Don't hold the lock while constructing the ringtone, since it can be slow. The caller
- // shouldn't call play on the same ringtone from 2 threads, so this shouldn't race and
- // waste the build.
- if (client == null) {
- final UserHandle user = Binder.getCallingUserHandle();
- Ringtone ringtone = Ringtone.createV1WithCustomAudioAttributes(
- getContextForUser(user), aa, uri, volumeShaperConfig,
- /* allowRemote= */ false);
- synchronized (mClients) {
- client = mClients.get(token);
- if (client == null) {
- client = new Client(token, ringtone);
- token.linkToDeath(client, 0);
- mClients.put(token, client);
- ringtone = null; // "owned" by the client now.
- }
- }
- // Clean up ringtone if it was abandoned (a client already existed).
- if (ringtone != null) {
- ringtone.stop();
- }
- }
- client.mRingtone.setLooping(looping);
- client.mRingtone.setVolume(volume);
- client.mRingtone.play();
+ playRemoteRingtone(token, uri, aa, true, Ringtone.MEDIA_SOUND,
+ null, volume, looping, /* hapticGenerator= */ false,
+ null);
}
@Override
@@ -169,7 +125,7 @@ public class RingtonePlayer implements CoreStartable {
@Nullable VolumeShaper.Configuration volumeShaperConfig)
throws RemoteException {
if (LOGD) {
- Log.d(TAG, "playRemoteRingtone(token=" + token + ", uri=" + uri + ", uid="
+ Log.d(TAG, "play(token=" + token + ", uri=" + uri + ", uid="
+ Binder.getCallingUid() + ")");
}
@@ -234,21 +190,6 @@ public class RingtonePlayer implements CoreStartable {
return false;
}
}
- @Override
- public void setPlaybackProperties(IBinder token, float volume, boolean looping,
- boolean hapticGeneratorEnabled) {
- // RingtoneV1-exclusive path.
- Client client;
- synchronized (mClients) {
- client = mClients.get(token);
- }
- if (client != null) {
- client.mRingtone.setVolume(volume);
- client.mRingtone.setLooping(looping);
- client.mRingtone.setHapticGeneratorEnabled(hapticGeneratorEnabled);
- }
- // else no client for token when setting playback properties but will be set at play()
- }
@Override
public void setHapticGeneratorEnabled(IBinder token, boolean hapticGeneratorEnabled) {