diff options
| author | 2024-02-05 13:48:07 +0000 | |
|---|---|---|
| committer | 2024-02-05 14:10:48 +0000 | |
| commit | 7f9dd2b5f686614a8b645719ee1a917cbbe80aad (patch) | |
| tree | 8a810e14b68d11727cae27311ce37afcf02d53cb | |
| parent | e53fa3e83bc80cc5acadab68617b8043884e567f (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.aidl | 8 | ||||
| -rw-r--r-- | media/java/android/media/LocalRingtonePlayer.java | 244 | ||||
| -rw-r--r-- | media/java/android/media/Ringtone.java | 453 | ||||
| -rw-r--r-- | media/java/android/media/RingtoneManager.java | 7 | ||||
| -rw-r--r-- | packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java | 38 |
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 |