diff --git a/media/java/android/media/Ringtone.java b/media/java/android/media/Ringtone.java index 82c3139bb4d33..538e64cf095cb 100644 --- a/media/java/android/media/Ringtone.java +++ b/media/java/android/media/Ringtone.java @@ -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)) { diff --git a/media/java/android/media/RingtoneManager.java b/media/java/android/media/RingtoneManager.java index 27db41cb9f4e5..f15f4438d671f 100644 --- a/media/java/android/media/RingtoneManager.java +++ b/media/java/android/media/RingtoneManager.java @@ -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";