From cfcf10324a1fbb2fe5d24ace2f67c6244e4d6f61 Mon Sep 17 00:00:00 2001 From: jiabin Date: Thu, 1 Jul 2021 16:30:50 -0700 Subject: [PATCH] Add context in AudioManager.hasHapticChannels. A context will be required for resolving a Uri. Using the context in AudioService may not always work and it may cause not oneway binder call issue. In that case, using the context in RingtoneManager could help. In AudioManager, caching a static context so that it can be used when there is not context provided. Test: atest RingtoneManagerTest Bug: 187842281 Change-Id: Ia2f514cb8f821926cf3d353cf912a44aeb584ab6 --- media/java/android/media/AudioManager.java | 52 +++++++++++++++++-- media/java/android/media/Ringtone.java | 2 +- media/java/android/media/RingtoneManager.java | 9 ++-- .../android/server/audio/AudioService.java | 20 +------ 4 files changed, 57 insertions(+), 26 deletions(-) diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index b7ea14ecd5d66..09ebb40e7ad7d 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -103,6 +103,8 @@ public class AudioManager { private static final AudioVolumeGroupChangeHandler sAudioAudioVolumeGroupChangedHandler = new AudioVolumeGroupChangeHandler(); + private static Context sContext; + /** * Broadcast intent, a hint for applications that audio is about to become * 'noisy' due to a change in audio outputs. For example, this intent may @@ -798,6 +800,7 @@ public class AudioManager { } else { mOriginalContext = context; } + sContext = context; } @UnsupportedAppUsage @@ -7220,15 +7223,56 @@ public class AudioManager { /** * Return if an asset contains haptic channels or not. + * + * @param context the {@link Context} to resolve the uri. * @param uri the {@link Uri} of the asset. * @return true if the assert contains haptic channels. * @hide */ - public static boolean hasHapticChannels(Uri uri) { + public static boolean hasHapticChannelsImpl(@NonNull Context context, @NonNull Uri uri) { + MediaExtractor extractor = new MediaExtractor(); try { - return getService().hasHapticChannels(uri); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); + extractor.setDataSource(context, uri, null); + for (int i = 0; i < extractor.getTrackCount(); i++) { + MediaFormat format = extractor.getTrackFormat(i); + if (format.containsKey(MediaFormat.KEY_HAPTIC_CHANNEL_COUNT) + && format.getInteger(MediaFormat.KEY_HAPTIC_CHANNEL_COUNT) > 0) { + return true; + } + } + } catch (IOException e) { + Log.e(TAG, "hasHapticChannels failure:" + e); + } + return false; + } + + /** + * Return if an asset contains haptic channels or not. + * + * @param context the {@link Context} to resolve the uri. + * @param uri the {@link Uri} of the asset. + * @return true if the assert contains haptic channels. + * @hide + */ + public static boolean hasHapticChannels(@Nullable Context context, @NonNull Uri uri) { + Objects.requireNonNull(uri); + if (context != null) { + return hasHapticChannelsImpl(context, uri); + } else if (sContext != null) { + if (DEBUG) { + Log.d(TAG, "Try to use static context to query if having haptic channels"); + } + return hasHapticChannelsImpl(sContext, uri); + } else { + // Try with audio service context, this may fail to get correct result. + if (DEBUG) { + Log.d(TAG, "Try to use audio service context to query if having haptic channels"); + } + try { + return getService().hasHapticChannels(uri); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } } } diff --git a/media/java/android/media/Ringtone.java b/media/java/android/media/Ringtone.java index f8297bc93b727..3cf03417334b8 100644 --- a/media/java/android/media/Ringtone.java +++ b/media/java/android/media/Ringtone.java @@ -407,7 +407,7 @@ 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(mUri) + boolean isHapticOnly = AudioManager.hasHapticChannels(mContext, mUri) && !mAudioAttributes.areHapticChannelsMuted() && mVolume == 0; if (isHapticOnly || mAudioManager.getStreamVolume( AudioAttributes.toLegacyStreamType(mAudioAttributes)) != 0) { diff --git a/media/java/android/media/RingtoneManager.java b/media/java/android/media/RingtoneManager.java index be6ff1baadd61..4ec79b7e085a2 100644 --- a/media/java/android/media/RingtoneManager.java +++ b/media/java/android/media/RingtoneManager.java @@ -1082,18 +1082,21 @@ public class RingtoneManager { * @return true if the ringtone contains haptic channels. */ public boolean hasHapticChannels(int position) { - return hasHapticChannels(getRingtoneUri(position)); + return AudioManager.hasHapticChannels(mContext, getRingtoneUri(position)); } /** * Returns if the {@link Ringtone} from a given sound URI contains - * haptic channels or not. + * haptic channels or not. As this function doesn't has a context + * to resolve the uri, the result may be wrong if the uri cannot be + * resolved correctly. + * Use {@link #hasHapticChannels(int)} instead when possible. * * @param ringtoneUri The {@link Uri} of a sound or ringtone. * @return true if the ringtone contains haptic channels. */ public static boolean hasHapticChannels(@NonNull Uri ringtoneUri) { - return AudioManager.hasHapticChannels(ringtoneUri); + return AudioManager.hasHapticChannels(null, ringtoneUri); } /** diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 136916af109db..91283b9c6ccca 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -95,8 +95,6 @@ import android.media.IRecordingConfigDispatcher; import android.media.IRingtonePlayer; import android.media.IStrategyPreferredDevicesDispatcher; import android.media.IVolumeController; -import android.media.MediaExtractor; -import android.media.MediaFormat; import android.media.MediaMetrics; import android.media.MediaRecorder.AudioSource; import android.media.PlayerBase; @@ -163,7 +161,6 @@ import com.android.server.pm.UserManagerService; import com.android.server.wm.ActivityTaskManagerInternal; import java.io.FileDescriptor; -import java.io.IOException; import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -6305,23 +6302,10 @@ public class AudioService extends IAudioService.Stub } /** - * See AudioManager.hasHapticChannels(Uri). + * See AudioManager.hasHapticChannels(Context, Uri). */ public boolean hasHapticChannels(Uri uri) { - MediaExtractor extractor = new MediaExtractor(); - try { - extractor.setDataSource(mContext, uri, null); - for (int i = 0; i < extractor.getTrackCount(); i++) { - MediaFormat format = extractor.getTrackFormat(i); - if (format.containsKey(MediaFormat.KEY_HAPTIC_CHANNEL_COUNT) - && format.getInteger(MediaFormat.KEY_HAPTIC_CHANNEL_COUNT) > 0) { - return true; - } - } - } catch (IOException e) { - Log.e(TAG, "hasHapticChannels failure:" + e); - } - return false; + return AudioManager.hasHapticChannelsImpl(mContext, uri); } ///////////////////////////////////////////////////////////////////////////