Reuse ringtone across multiple ringer operations

This patch avoids multiple ringtone loads when taking place
that were redundant as previous approach instantiated the
same ringtone multiple times over the play flow instead we
gather all the requirements for a ringtone and instantiate
it once.

Bug: 240621827
Test: atest RingtoneTest
Test: atest RingerTest
Test: atest IncomingCallTest
Test: Verified ringtone and vibration works in various forms when
receiving call.

Change-Id: I7ac0a52c99d48fb7241c55c709a9a876a6057319
This commit is contained in:
Edgar Arriaga
2022-09-07 12:22:10 -07:00
parent 5f35907147
commit 833b169eeb
2 changed files with 71 additions and 38 deletions

View File

@@ -158,13 +158,15 @@ public class Ringtone {
/**
* 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 void createLocalMediaPlayer() {
public boolean createLocalMediaPlayer() {
Trace.beginSection("createLocalMediaPlayer");
if (mUri == null) {
Log.e(TAG, "Could not create media player as no URI was provided.");
return;
return mAllowRemote && mRemotePlayer != null;
}
destroyLocalPlayer();
// try opening uri locally before delegating to remote player
@@ -195,6 +197,30 @@ public class Ringtone {
}
}
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;
}
/**
@@ -423,7 +449,6 @@ public class Ringtone {
*/
public void setUri(Uri uri, @Nullable VolumeShaper.Configuration volumeShaperConfig) {
mVolumeShaperConfig = volumeShaperConfig;
mUri = uri;
if (mUri == null) {
destroyLocalPlayer();
@@ -443,10 +468,11 @@ public class Ringtone {
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).
boolean isHapticOnly = AudioManager.hasHapticChannels(mContext, mUri)
&& !mAudioAttributes.areHapticChannelsMuted() && mVolume == 0;
if (isHapticOnly || mAudioManager.getStreamVolume(
AudioAttributes.toLegacyStreamType(mAudioAttributes)) != 0) {
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)) {

View File

@@ -720,11 +720,14 @@ public class RingtoneManager {
@Nullable VolumeShaper.Configuration volumeShaperConfig,
AudioAttributes audioAttributes) {
// Don't set the stream type
Ringtone ringtone =
getRingtone(context, ringtoneUri, -1 /* streamType */, volumeShaperConfig, false);
Ringtone ringtone = getRingtone(context, ringtoneUri, -1 /* streamType */,
volumeShaperConfig, false);
if (ringtone != null) {
ringtone.setAudioAttributesField(audioAttributes);
ringtone.createLocalMediaPlayer();
if (!ringtone.createLocalMediaPlayer()) {
Log.e(TAG, "Failed to open ringtone " + ringtoneUri);
return null;
}
}
return ringtone;
}
@@ -750,19 +753,6 @@ public class RingtoneManager {
createLocalMediaPlayer);
}
//FIXME bypass the notion of stream types within the class
/**
* Returns a {@link Ringtone} with {@link VolumeShaper} if required for a given sound URI on
* the given stream type. Normally, if you change the stream type on the returned
* {@link Ringtone}, it will re-create the {@link MediaPlayer}. This is just
* an optimized route to avoid that.
*
* @param streamType The stream type for the ringtone, or -1 if it should
* not be set (and the default used instead).
* @param volumeShaperConfig config for volume shaper of the ringtone if applied.
* @see #getRingtone(Context, Uri)
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private static Ringtone getRingtone(final Context context, Uri ringtoneUri, int streamType,
@Nullable VolumeShaper.Configuration volumeShaperConfig,
boolean createLocalMediaPlayer) {
@@ -776,7 +766,10 @@ public class RingtoneManager {
r.setVolumeShaperConfig(volumeShaperConfig);
r.setUri(ringtoneUri, volumeShaperConfig);
if (createLocalMediaPlayer) {
r.createLocalMediaPlayer();
if (!r.createLocalMediaPlayer()) {
Log.e(TAG, "Failed to open ringtone " + ringtoneUri);
return null;
}
}
return r;
} catch (Exception ex) {
@@ -1158,24 +1151,38 @@ public class RingtoneManager {
}
// Try finding the scanned ringtone
final String filename = getDefaultRingtoneFilename(type);
final String whichAudio = getQueryStringForType(type);
final String where = MediaColumns.DISPLAY_NAME + "=? AND " + whichAudio + "=?";
final Uri baseUri = MediaStore.Audio.Media.INTERNAL_CONTENT_URI;
try (Cursor cursor = context.getContentResolver().query(baseUri,
new String[] { MediaColumns._ID },
where,
new String[] { filename, "1" }, null)) {
if (cursor.moveToFirst()) {
final Uri ringtoneUri = context.getContentResolver().canonicalizeOrElse(
ContentUris.withAppendedId(baseUri, cursor.getLong(0)));
RingtoneManager.setActualDefaultRingtoneUri(context, type, ringtoneUri);
Settings.System.putInt(context.getContentResolver(), setting, 1);
}
Uri ringtoneUri = computeDefaultRingtoneUri(context, type);
if (ringtoneUri != null) {
RingtoneManager.setActualDefaultRingtoneUri(context, type, ringtoneUri);
Settings.System.putInt(context.getContentResolver(), setting, 1);
}
}
}
/**
* @param type the type of ringtone (e.g {@link #TYPE_RINGTONE})
* @return the system default URI if found, null otherwise.
*/
private static Uri computeDefaultRingtoneUri(@NonNull Context context, int type) {
// Try finding the scanned ringtone
final String filename = getDefaultRingtoneFilename(type);
final String whichAudio = getQueryStringForType(type);
final String where = MediaColumns.DISPLAY_NAME + "=? AND " + whichAudio + "=?";
final Uri baseUri = MediaStore.Audio.Media.INTERNAL_CONTENT_URI;
try (Cursor cursor = context.getContentResolver().query(baseUri,
new String[] { MediaColumns._ID },
where,
new String[] { filename, "1" }, null)) {
if (cursor.moveToFirst()) {
final Uri ringtoneUri = context.getContentResolver().canonicalizeOrElse(
ContentUris.withAppendedId(baseUri, cursor.getLong(0)));
return ringtoneUri;
}
}
return null;
}
private static String getDefaultRingtoneSetting(int type) {
switch (type) {
case TYPE_RINGTONE: return "ringtone_set";