summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Adrian Roos <roosa@google.com> 2024-02-05 13:48:07 +0000
committer Adrian Roos <roosa@google.com> 2024-02-05 14:10:48 +0000
commit7f9dd2b5f686614a8b645719ee1a917cbbe80aad (patch)
tree8a810e14b68d11727cae27311ce37afcf02d53cb
parente53fa3e83bc80cc5acadab68617b8043884e567f (diff)
Revert "Factor out ringtone local vs remote playback into separate implementations."
This reverts commit cb110af311e28c92f40abd08816aa577c18135fc. Change-Id: I9dd4c942af3ec2ff0bd0f1aa8908cb78d1788316 Bug: 323853450
-rw-r--r--media/java/android/media/IRingtonePlayer.aidl8
-rw-r--r--media/java/android/media/LocalRingtonePlayer.java244
-rw-r--r--media/java/android/media/Ringtone.java453
-rw-r--r--media/java/android/media/RingtoneManager.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java38
5 files changed, 238 insertions, 512 deletions
diff --git a/media/java/android/media/IRingtonePlayer.aidl b/media/java/android/media/IRingtonePlayer.aidl
index fbe588257999..c96a400407dc 100644
--- a/media/java/android/media/IRingtonePlayer.aidl
+++ b/media/java/android/media/IRingtonePlayer.aidl
@@ -30,13 +30,11 @@ interface IRingtonePlayer {
@UnsupportedAppUsage
oneway void play(IBinder token, in Uri uri, in AudioAttributes aa, float volume, boolean looping);
oneway void playWithVolumeShaping(IBinder token, in Uri uri, in AudioAttributes aa,
- float volume, boolean looping, boolean hapticGeneratorEnabled,
- in @nullable VolumeShaper.Configuration volumeShaperConfig);
+ float volume, boolean looping, 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);
+ oneway void setPlaybackProperties(IBinder token, float volume, boolean looping,
+ boolean hapticGeneratorEnabled);
/** Used for Notification sound playback. */
oneway void playAsync(in Uri uri, in UserHandle user, boolean looping, in AudioAttributes aa, float volume);
diff --git a/media/java/android/media/LocalRingtonePlayer.java b/media/java/android/media/LocalRingtonePlayer.java
deleted file mode 100644
index 87de32d14e16..000000000000
--- a/media/java/android/media/LocalRingtonePlayer.java
+++ /dev/null
@@ -1,244 +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.NonNull;
-import android.annotation.Nullable;
-import android.content.Context;
-import android.content.res.AssetFileDescriptor;
-import android.media.audiofx.HapticGenerator;
-import android.net.Uri;
-import android.os.Trace;
-import android.util.Log;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Objects;
-
-/**
- * Plays a ringtone on the local process.
- * @hide
- */
-public class LocalRingtonePlayer
- implements Ringtone.RingtonePlayer, MediaPlayer.OnCompletionListener {
- private static final String TAG = "LocalRingtonePlayer";
-
- // keep references on active Ringtones until stopped or completion listener called.
- private static final ArrayList<LocalRingtonePlayer> sActiveRingtones = new ArrayList<>();
-
- private final MediaPlayer mMediaPlayer;
- private final AudioAttributes mAudioAttributes;
- private final AudioManager mAudioManager;
- private final VolumeShaper mVolumeShaper;
- private HapticGenerator mHapticGenerator;
-
- private LocalRingtonePlayer(@NonNull MediaPlayer mediaPlayer,
- @NonNull AudioAttributes audioAttributes, @NonNull AudioManager audioManager,
- @Nullable HapticGenerator hapticGenerator, @Nullable VolumeShaper volumeShaper) {
- Objects.requireNonNull(mediaPlayer);
- Objects.requireNonNull(audioAttributes);
- Objects.requireNonNull(audioManager);
- mMediaPlayer = mediaPlayer;
- mAudioAttributes = audioAttributes;
- mAudioManager = audioManager;
- mVolumeShaper = volumeShaper;
- mHapticGenerator = hapticGenerator;
- }
-
- /**
- * Creates a {@link LocalRingtonePlayer} for a Uri, returning null if the Uri can't be
- * loaded in the local player.
- */
- @Nullable
- static LocalRingtonePlayer create(@NonNull Context context,
- @NonNull AudioManager audioManager, @NonNull Uri soundUri,
- @NonNull AudioAttributes audioAttributes,
- @Nullable VolumeShaper.Configuration volumeShaperConfig,
- @Nullable AudioDeviceInfo preferredDevice, boolean initialHapticGeneratorEnabled,
- boolean initialLooping, float initialVolume) {
- Objects.requireNonNull(context);
- Objects.requireNonNull(soundUri);
- Objects.requireNonNull(audioAttributes);
- Trace.beginSection("createLocalMediaPlayer");
- MediaPlayer mediaPlayer = new MediaPlayer();
- HapticGenerator hapticGenerator = null;
- try {
- mediaPlayer.setDataSource(context, soundUri);
- mediaPlayer.setAudioAttributes(audioAttributes);
- mediaPlayer.setPreferredDevice(preferredDevice);
- mediaPlayer.setLooping(initialLooping);
- mediaPlayer.setVolume(initialVolume);
- if (initialHapticGeneratorEnabled) {
- hapticGenerator = HapticGenerator.create(mediaPlayer.getAudioSessionId());
- hapticGenerator.setEnabled(true);
- }
- VolumeShaper volumeShaper = null;
- if (volumeShaperConfig != null) {
- volumeShaper = mediaPlayer.createVolumeShaper(volumeShaperConfig);
- }
- mediaPlayer.prepare();
- return new LocalRingtonePlayer(mediaPlayer, audioAttributes, audioManager,
- hapticGenerator, volumeShaper);
- } catch (SecurityException | IOException e) {
- if (hapticGenerator != null) {
- hapticGenerator.release();
- }
- // volume shaper closes with media player
- mediaPlayer.release();
- return null;
- } finally {
- Trace.endSection();
- }
- }
-
- /**
- * Creates a {@link LocalRingtonePlayer} for an externally referenced file descriptor. This is
- * intended for loading a fallback from an internal resource, rather than via a Uri.
- */
- @Nullable
- static LocalRingtonePlayer createForFallback(
- @NonNull AudioManager audioManager, @NonNull AssetFileDescriptor afd,
- @NonNull AudioAttributes audioAttributes,
- @Nullable VolumeShaper.Configuration volumeShaperConfig,
- @Nullable AudioDeviceInfo preferredDevice,
- boolean initialLooping, float initialVolume) {
- // Haptic generator not supported for fallback.
- Objects.requireNonNull(audioManager);
- Objects.requireNonNull(afd);
- Objects.requireNonNull(audioAttributes);
- Trace.beginSection("createFallbackLocalMediaPlayer");
-
- MediaPlayer mediaPlayer = new MediaPlayer();
- try {
- if (afd.getDeclaredLength() < 0) {
- mediaPlayer.setDataSource(afd.getFileDescriptor());
- } else {
- mediaPlayer.setDataSource(afd.getFileDescriptor(),
- afd.getStartOffset(),
- afd.getDeclaredLength());
- }
- mediaPlayer.setAudioAttributes(audioAttributes);
- mediaPlayer.setPreferredDevice(preferredDevice);
- mediaPlayer.setLooping(initialLooping);
- mediaPlayer.setVolume(initialVolume);
- VolumeShaper volumeShaper = null;
- if (volumeShaperConfig != null) {
- volumeShaper = mediaPlayer.createVolumeShaper(volumeShaperConfig);
- }
- mediaPlayer.prepare();
- return new LocalRingtonePlayer(mediaPlayer, audioAttributes, audioManager,
- /* hapticGenerator= */ null, volumeShaper);
- } catch (SecurityException | IOException e) {
- Log.e(TAG, "Failed to open fallback ringtone");
- mediaPlayer.release();
- return null;
- } finally {
- Trace.endSection();
- }
- }
-
- @Override
- public boolean play() {
- // 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 && (mAudioAttributes.areHapticChannelsMuted() || !hasHapticChannels())) {
- return true; // Successfully played while muted.
- }
- synchronized (sActiveRingtones) {
- sActiveRingtones.add(this);
- }
-
- mMediaPlayer.setOnCompletionListener(this);
- mMediaPlayer.start();
- if (mVolumeShaper != null) {
- mVolumeShaper.apply(VolumeShaper.Operation.PLAY);
- }
- return true;
- }
-
- @Override
- public boolean isPlaying() {
- return mMediaPlayer.isPlaying();
- }
-
- @Override
- public void stopAndRelease() {
- synchronized (sActiveRingtones) {
- sActiveRingtones.remove(this);
- }
- if (mHapticGenerator != null) {
- mHapticGenerator.release();
- }
- mMediaPlayer.setOnCompletionListener(null);
- mMediaPlayer.reset();
- mMediaPlayer.release();
- }
-
- @Override
- public void setPreferredDevice(@Nullable AudioDeviceInfo audioDeviceInfo) {
- mMediaPlayer.setPreferredDevice(audioDeviceInfo);
- }
-
- @Override
- public void setLooping(boolean looping) {
- mMediaPlayer.setLooping(looping);
- }
-
- @Override
- public void setHapticGeneratorEnabled(boolean enabled) {
- if (enabled && mHapticGenerator == null) {
- mHapticGenerator = HapticGenerator.create(mMediaPlayer.getAudioSessionId());
- }
- if (mHapticGenerator != null) {
- mHapticGenerator.setEnabled(enabled);
- }
- }
-
- @Override
- public void setVolume(float volume) {
- mMediaPlayer.setVolume(volume);
- }
-
- /**
- * Same as AudioManager.hasHapticChannels except it assumes an already created ringtone.
- * @hide
- */
- @Override
- public boolean hasHapticChannels() {
- // FIXME: support remote player, or internalize haptic channels support and remove entirely.
- try {
- Trace.beginSection("LocalRingtonePlayer.hasHapticChannels");
- for (MediaPlayer.TrackInfo trackInfo : mMediaPlayer.getTrackInfo()) {
- if (trackInfo.hasHapticChannels()) {
- return true;
- }
- }
- } finally {
- Trace.endSection();
- }
- return false;
- }
-
- @Override
- public void onCompletion(MediaPlayer mp) {
- synchronized (sActiveRingtones) {
- sActiveRingtones.remove(this);
- }
- mp.setOnCompletionListener(null); // Help the Java GC: break the refcount cycle.
- }
-}
diff --git a/media/java/android/media/Ringtone.java b/media/java/android/media/Ringtone.java
index c423c88c6d59..e78dc31646ca 100644
--- a/media/java/android/media/Ringtone.java
+++ b/media/java/android/media/Ringtone.java
@@ -16,7 +16,6 @@
package android.media;
-import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ContentProvider;
@@ -28,15 +27,16 @@ import android.database.Cursor;
import android.media.audiofx.HapticGenerator;
import android.net.Uri;
import android.os.Binder;
-import android.os.IBinder;
+import android.os.Build;
import android.os.RemoteException;
import android.os.Trace;
import android.provider.MediaStore;
import android.provider.MediaStore.MediaColumns;
import android.provider.Settings;
import android.util.Log;
-
+import com.android.internal.annotations.VisibleForTesting;
import java.io.IOException;
+import java.util.ArrayList;
/**
* Ringtone provides a quick method for playing a ringtone, notification, or
@@ -49,6 +49,7 @@ import java.io.IOException;
*/
public class Ringtone {
private static final String TAG = "Ringtone";
+ private static final boolean LOGD = true;
private static final String[] MEDIA_COLUMNS = new String[] {
MediaStore.Audio.Media._ID,
@@ -58,17 +59,27 @@ public class Ringtone {
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<Ringtone> sActiveRingtones = new ArrayList<Ringtone>();
+
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 #mRemoteRingtoneService}. Typically this is false when we're the remote
+ * {@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 mRemoteRingtoneService;
+ 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;
@@ -79,7 +90,6 @@ public class Ringtone {
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.build();
private boolean mPreferBuiltinDevice;
- private RingtonePlayer mActivePlayer;
// playback properties, use synchronized with mPlaybackSettingsLock
private boolean mIsLooping = false;
private float mVolume = 1.0f;
@@ -91,8 +101,9 @@ public class Ringtone {
public Ringtone(Context context, boolean allowRemote) {
mContext = context;
mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
- mRemoteRingtoneService = allowRemote ? mAudioManager.getRingtonePlayer() : null;
- mAllowRemote = allowRemote && (mRemoteRingtoneService != null);
+ mAllowRemote = allowRemote;
+ mRemotePlayer = allowRemote ? mAudioManager.getRingtonePlayer() : null;
+ mRemoteToken = allowRemote ? new Binder() : null;
}
/**
@@ -127,14 +138,11 @@ public class Ringtone {
*/
public void setAudioAttributes(AudioAttributes attributes)
throws IllegalArgumentException {
- // TODO: deprecate this method - it will be done with a builder.
setAudioAttributesField(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();
- }
+ // The audio attributes have to be set before the media player is prepared.
+ // Re-initialize it.
+ setUri(mUri, mVolumeShaperConfig);
+ createLocalMediaPlayer();
}
/**
@@ -168,16 +176,16 @@ public class Ringtone {
}
/**
- * Sets the preferred device of the ringtone playback to the built-in device.
+ * Sets the preferred device of the ringtong playback to the built-in device.
*
* @hide
*/
public boolean preferBuiltinDevice(boolean enable) {
mPreferBuiltinDevice = enable;
- if (mActivePlayer != null) {
- mActivePlayer.setPreferredDevice(enable ? getBuiltinDevice(mAudioManager) : null);
+ if (mLocalPlayer == null) {
+ return true;
}
- return true; // FIXME: Unused, to clean up with builder.
+ return mLocalPlayer.setPreferredDevice(getBuiltinDevice(mAudioManager));
}
/**
@@ -186,34 +194,44 @@ public class Ringtone {
* 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");
- if (mActivePlayer != null) {
- stopAndReleaseActivePlayer();
- }
-
+ public boolean createLocalMediaPlayer() {
+ Trace.beginSection("createLocalMediaPlayer");
if (mUri == null) {
Log.e(TAG, "Could not create media player as no URI was provided.");
- return mAllowRemote;
+ 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();
- AudioDeviceInfo preferredDevice =
- mPreferBuiltinDevice ? getBuiltinDevice(mAudioManager) : null;
- mActivePlayer = LocalRingtonePlayer.create(mContext, mAudioManager, mUri, mAudioAttributes,
- mVolumeShaperConfig, preferredDevice, mHapticGeneratorEnabled, mIsLooping, mVolume);
- if (mActivePlayer != null) {
- return true;
+ } catch (SecurityException | IOException e) {
+ destroyLocalPlayer();
+ if (!mAllowRemote) {
+ Log.w(TAG, "Remote playback not allowed: " + e);
+ }
}
- // Local player failed, setup a remote one if possible.
- if (!mAllowRemote) {
- return false;
+ if (LOGD) {
+ if (mLocalPlayer != null) {
+ Log.d(TAG, "Successfully created local player");
+ } else {
+ Log.d(TAG, "Problem opening; delegating to remote player");
+ }
}
-
- mActivePlayer = new RemoteRingtonePlayer(mRemoteRingtoneService, mUri, mAudioAttributes,
- mVolumeShaperConfig, mHapticGeneratorEnabled, mIsLooping, mVolume);
- return true;
+ Trace.endSection();
+ return mLocalPlayer != null || (mAllowRemote && mRemotePlayer != null);
}
/**
@@ -223,7 +241,29 @@ public class Ringtone {
* @hide
*/
public boolean hasHapticChannels() {
- return (mActivePlayer == null) ? false : mActivePlayer.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;
}
/**
@@ -242,9 +282,7 @@ public class Ringtone {
public void setLooping(boolean looping) {
synchronized (mPlaybackSettingsLock) {
mIsLooping = looping;
- if (mActivePlayer != null) {
- mActivePlayer.setLooping(looping);
- }
+ applyPlaybackProperties_sync();
}
}
@@ -264,17 +302,11 @@ public class Ringtone {
* corresponds to no attenuation being applied.
*/
public void setVolume(float volume) {
- if (volume < 0.0f) {
- volume = 0.0f;
- } else if (volume > 1.0f) {
- volume = 1.0f;
- }
-
synchronized (mPlaybackSettingsLock) {
+ if (volume < 0.0f) { volume = 0.0f; }
+ if (volume > 1.0f) { volume = 1.0f; }
mVolume = volume;
- if (mActivePlayer != null) {
- mActivePlayer.setVolume(volume);
- }
+ applyPlaybackProperties_sync();
}
}
@@ -301,9 +333,7 @@ public class Ringtone {
}
synchronized (mPlaybackSettingsLock) {
mHapticGeneratorEnabled = enabled;
- if (mActivePlayer != null) {
- mActivePlayer.setHapticGeneratorEnabled(enabled);
- }
+ applyPlaybackProperties_sync();
}
return true;
}
@@ -319,6 +349,32 @@ public class Ringtone {
}
/**
+ * 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
*
@@ -429,7 +485,7 @@ public class Ringtone {
mVolumeShaperConfig = volumeShaperConfig;
mUri = uri;
if (mUri == null) {
- stopAndReleaseActivePlayer();
+ destroyLocalPlayer();
}
}
@@ -443,16 +499,36 @@ public class Ringtone {
* Plays the ringtone.
*/
public void play() {
- if (mActivePlayer != null) {
- if (mActivePlayer.play()) {
- return;
- } else {
- // Discard active player: play() is only meant to be called once.
- stopAndReleaseActivePlayer();
+ 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");
}
- }
- if (!playFallbackRingtone()) {
- Log.w(TAG, "Neither local nor remote playback available");
}
}
@@ -460,13 +536,45 @@ public class Ringtone {
* Stops a playing ringtone.
*/
public void stop() {
- stopAndReleaseActivePlayer();
+ 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 stopAndReleaseActivePlayer() {
- if (mActivePlayer != null) {
- mActivePlayer.stopAndRelease();
- mActivePlayer = null;
+ private void startLocalPlayer() {
+ if (mLocalPlayer == null) {
+ return;
+ }
+ synchronized (sActiveRingtones) {
+ sActiveRingtones.add(this);
+ }
+ mLocalPlayer.setOnCompletionListener(mCompletionListener);
+ mLocalPlayer.start();
+ if (mVolumeShaper != null) {
+ mVolumeShaper.apply(VolumeShaper.Operation.PLAY);
}
}
@@ -476,22 +584,24 @@ public class Ringtone {
* @return True if playing, false otherwise.
*/
public boolean isPlaying() {
- if (mActivePlayer != null) {
- return mActivePlayer.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, "No active ringtone player");
+ Log.w(TAG, "Neither local nor remote playback available");
return false;
}
}
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;
}
int ringtoneType = RingtoneManager.getDefaultType(mUri);
@@ -501,41 +611,40 @@ public class Ringtone {
return false;
}
// Default ringtone, try fallback ringtone.
- AssetFileDescriptor afd;
try {
- afd = mContext.getResources().openRawResourceFd(
+ AssetFileDescriptor afd = mContext.getResources().openRawResourceFd(
com.android.internal.R.raw.fallbackring);
- } catch (NotFoundException nfe) {
- Log.e(TAG, "Fallback ringtone does not exist");
- return false;
- }
- if (afd == null) {
- Log.e(TAG, "Could not load fallback ringtone");
- return false;
- }
-
- try {
- AudioDeviceInfo preferredDevice =
- mPreferBuiltinDevice ? getBuiltinDevice(mAudioManager) : null;
- mActivePlayer = LocalRingtonePlayer.createForFallback(mAudioManager, afd,
- mAudioAttributes, mVolumeShaperConfig, preferredDevice, mIsLooping, mVolume);
- if (mActivePlayer == null) {
+ if (afd == null) {
+ Log.e(TAG, "Could not load fallback ringtone");
return false;
- } else if (mActivePlayer.play()) {
- return true;
+ }
+ mLocalPlayer = new MediaPlayer();
+ if (afd.getDeclaredLength() < 0) {
+ mLocalPlayer.setDataSource(afd.getFileDescriptor());
} else {
- stopAndReleaseActivePlayer();
- return false;
+ mLocalPlayer.setDataSource(afd.getFileDescriptor(),
+ afd.getStartOffset(),
+ afd.getDeclaredLength());
}
- } finally {
- try {
- afd.close();
- } 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 closing fallback ringtone");
+ 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;
}
void setTitle(String title) {
@@ -544,134 +653,18 @@ public class Ringtone {
@Override
protected void finalize() {
- if (mActivePlayer != null) {
- mActivePlayer.stopAndRelease();
+ if (mLocalPlayer != null) {
+ mLocalPlayer.release();
}
}
- /**
- * 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 VolumeShaper.Configuration mVolumeShaperConfig;
- private final AudioAttributes mAudioAttributes;
- private boolean mIsLooping;
- private float mVolume;
- private boolean mIsHapticGeneratorEnabled;
-
- RemoteRingtonePlayer(@NonNull IRingtonePlayer remoteRingtoneService,
- @NonNull Uri uri, @NonNull AudioAttributes audioAttributes,
- @Nullable VolumeShaper.Configuration volumeShaperConfig,
- boolean isHapticGeneratorEnabled, boolean initialIsLooping, float initialVolume) {
- mRemoteRingtoneService = remoteRingtoneService;
- mCanonicalUri = uri.getCanonicalUri();
- mAudioAttributes = audioAttributes;
- mVolumeShaperConfig = volumeShaperConfig;
- mIsHapticGeneratorEnabled = isHapticGeneratorEnabled;
- mIsLooping = initialIsLooping;
- mVolume = initialVolume;
- }
-
- @Override
- public boolean play() {
- try {
- mRemoteRingtoneService.playWithVolumeShaping(mRemoteToken, mCanonicalUri,
- mAudioAttributes, mVolume, mIsLooping, mIsHapticGeneratorEnabled,
- 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) {
- mIsHapticGeneratorEnabled = enabled;
- try {
- mRemoteRingtoneService.setHapticGeneratorEnabled(mRemoteToken, enabled);
- } catch (RemoteException e) {
- Log.w(TAG, "Problem setting hapticGeneratorEnabled: " + e);
- }
- }
-
+ class MyOnCompletionListener implements MediaPlayer.OnCompletionListener {
@Override
- public void setVolume(float volume) {
- mVolume = volume;
- try {
- mRemoteRingtoneService.setVolume(mRemoteToken, volume);
- } catch (RemoteException e) {
- Log.w(TAG, "Problem setting volume: " + e);
+ public void onCompletion(MediaPlayer mp) {
+ synchronized (sActiveRingtones) {
+ sActiveRingtones.remove(Ringtone.this);
}
- }
-
- @Override
- public boolean hasHapticChannels() {
- // FIXME: support remote player, or internalize haptic channels support and remove
- // entirely.
- return false;
+ mp.setOnCompletionListener(null); // Help the Java GC: break the refcount cycle.
}
}
}
diff --git a/media/java/android/media/RingtoneManager.java b/media/java/android/media/RingtoneManager.java
index 6f31a47990a7..3432b3f5995a 100644
--- a/media/java/android/media/RingtoneManager.java
+++ b/media/java/android/media/RingtoneManager.java
@@ -16,6 +16,7 @@
package android.media;
+import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -810,7 +811,7 @@ public class RingtoneManager {
volumeShaperConfig, false);
if (ringtone != null) {
ringtone.setAudioAttributesField(audioAttributes);
- if (!ringtone.reinitializeActivePlayer()) {
+ if (!ringtone.createLocalMediaPlayer()) {
Log.e(TAG, "Failed to open ringtone " + ringtoneUri);
return null;
}
@@ -829,7 +830,7 @@ public class RingtoneManager {
* not be set (and the default used instead).
* @param createLocalMediaPlayer when true, the ringtone returned will be fully
* created otherwise, it will require the caller to create the media player manually
- * {@link Ringtone#reinitializeActivePlayer()} in order to play the Ringtone.
+ * {@link Ringtone#createLocalMediaPlayer()} in order to play the Ringtone.
* @see #getRingtone(Context, Uri)
*/
@UnsupportedAppUsage
@@ -852,7 +853,7 @@ public class RingtoneManager {
r.setVolumeShaperConfig(volumeShaperConfig);
r.setUri(ringtoneUri, volumeShaperConfig);
if (createLocalMediaPlayer) {
- if (!r.reinitializeActivePlayer()) {
+ if (!r.createLocalMediaPlayer()) {
Log.e(TAG, "Failed to open ringtone " + ringtoneUri);
return null;
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java b/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
index 54849f3fc68d..3ab0420e12b7 100644
--- a/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
@@ -99,7 +99,7 @@ public class RingtonePlayer implements CoreStartable {
mRingtone = new Ringtone(getContextForUser(user), false);
mRingtone.setAudioAttributesField(aa);
mRingtone.setUri(uri, volumeShaperConfig);
- mRingtone.reinitializeActivePlayer();
+ mRingtone.createLocalMediaPlayer();
}
@Override
@@ -116,14 +116,11 @@ public class RingtonePlayer implements CoreStartable {
@Override
public void play(IBinder token, Uri uri, AudioAttributes aa, float volume, boolean looping)
throws RemoteException {
- playWithVolumeShaping(token, uri, aa, volume, looping, /* hapticGenerator= */ false,
- null);
+ playWithVolumeShaping(token, uri, aa, volume, looping, null);
}
-
@Override
public void playWithVolumeShaping(IBinder token, Uri uri, AudioAttributes aa, float volume,
- boolean looping, boolean isHapticGeneratorEnabled,
- @Nullable VolumeShaper.Configuration volumeShaperConfig)
+ boolean looping, @Nullable VolumeShaper.Configuration volumeShaperConfig)
throws RemoteException {
if (LOGD) {
Log.d(TAG, "play(token=" + token + ", uri=" + uri + ", uid="
@@ -141,7 +138,6 @@ public class RingtonePlayer implements CoreStartable {
}
client.mRingtone.setLooping(looping);
client.mRingtone.setVolume(volume);
- client.mRingtone.setHapticGeneratorEnabled(isHapticGeneratorEnabled);
client.mRingtone.play();
}
@@ -173,36 +169,18 @@ public class RingtonePlayer implements CoreStartable {
}
@Override
- public void setHapticGeneratorEnabled(IBinder token, boolean hapticGeneratorEnabled) {
- Client client;
- synchronized (mClients) {
- client = mClients.get(token);
- }
- if (client != null) {
- client.mRingtone.setHapticGeneratorEnabled(hapticGeneratorEnabled);
- }
- }
-
- @Override
- public void setLooping(IBinder token, boolean looping) {
- Client client;
- synchronized (mClients) {
- client = mClients.get(token);
- }
- if (client != null) {
- client.mRingtone.setLooping(looping);
- }
- }
-
- @Override
- public void setVolume(IBinder token, float volume) {
+ public void setPlaybackProperties(IBinder token, float volume, boolean looping,
+ boolean hapticGeneratorEnabled) {
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