Add API for querying direct playback support.

Add API for querying direct playback support. Direct playback can be
compressed offload playback or direct bistream playback. This API
can help developers to discover device's capabilities.

Bug: 197838445
Test: atest AudioManagerTest
Change-Id: I1c04e6595c12559e17b3d68155b2d0e7d0909421
diff --git a/core/api/current.txt b/core/api/current.txt
index 020c8ff..bde7417 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -20749,11 +20749,12 @@
     method @NonNull public java.util.List<android.media.AudioDeviceInfo> getAvailableCommunicationDevices();
     method @Nullable public android.media.AudioDeviceInfo getCommunicationDevice();
     method public android.media.AudioDeviceInfo[] getDevices(int);
+    method public static int getDirectPlaybackSupport(@NonNull android.media.AudioFormat, @NonNull android.media.AudioAttributes);
     method public int getEncodedSurroundMode();
     method public java.util.List<android.media.MicrophoneInfo> getMicrophones() throws java.io.IOException;
     method public int getMode();
     method public String getParameters(String);
-    method public static int getPlaybackOffloadSupport(@NonNull android.media.AudioFormat, @NonNull android.media.AudioAttributes);
+    method @Deprecated public static int getPlaybackOffloadSupport(@NonNull android.media.AudioFormat, @NonNull android.media.AudioAttributes);
     method public String getProperty(String);
     method public int getRingerMode();
     method @Deprecated public int getRouting(int);
@@ -20844,6 +20845,10 @@
     field public static final int AUDIOFOCUS_REQUEST_FAILED = 0; // 0x0
     field public static final int AUDIOFOCUS_REQUEST_GRANTED = 1; // 0x1
     field public static final int AUDIO_SESSION_ID_GENERATE = 0; // 0x0
+    field public static final int DIRECT_PLAYBACK_BITSTREAM_SUPPORTED = 4; // 0x4
+    field public static final int DIRECT_PLAYBACK_NOT_SUPPORTED = 0; // 0x0
+    field public static final int DIRECT_PLAYBACK_OFFLOAD_GAPLESS_SUPPORTED = 3; // 0x3
+    field public static final int DIRECT_PLAYBACK_OFFLOAD_SUPPORTED = 1; // 0x1
     field public static final int ENCODED_SURROUND_OUTPUT_ALWAYS = 2; // 0x2
     field public static final int ENCODED_SURROUND_OUTPUT_AUTO = 0; // 0x0
     field public static final int ENCODED_SURROUND_OUTPUT_MANUAL = 3; // 0x3
@@ -21234,7 +21239,7 @@
     method public int getStreamType();
     method public boolean getTimestamp(android.media.AudioTimestamp);
     method public int getUnderrunCount();
-    method public static boolean isDirectPlaybackSupported(@NonNull android.media.AudioFormat, @NonNull android.media.AudioAttributes);
+    method @Deprecated public static boolean isDirectPlaybackSupported(@NonNull android.media.AudioFormat, @NonNull android.media.AudioAttributes);
     method public boolean isOffloadedPlayback();
     method public void pause() throws java.lang.IllegalStateException;
     method public void play() throws java.lang.IllegalStateException;
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 6b4d773..c5cb7f0 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1479,6 +1479,9 @@
     field public static final int DEVICE_ROLE_DISABLED = 2; // 0x2
     field public static final int DEVICE_ROLE_NONE = 0; // 0x0
     field public static final int DEVICE_ROLE_PREFERRED = 1; // 0x1
+    field public static final int DIRECT_BITSTREAM_SUPPORTED = 4; // 0x4
+    field public static final int DIRECT_OFFLOAD_GAPLESS_SUPPORTED = 3; // 0x3
+    field public static final int DIRECT_OFFLOAD_SUPPORTED = 1; // 0x1
     field public static final int OFFLOAD_GAPLESS_SUPPORTED = 2; // 0x2
     field public static final int OFFLOAD_SUPPORTED = 1; // 0x1
     field public static final int STREAM_DEFAULT = -1; // 0xffffffff
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 8b45907..0b0870d 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -2774,6 +2774,46 @@
     return canBeSpatialized;
 }
 
+// keep these values in sync with AudioSystem.java
+#define DIRECT_NOT_SUPPORTED 0
+#define DIRECT_OFFLOAD_SUPPORTED 1
+#define DIRECT_OFFLOAD_GAPLESS_SUPPORTED 3
+#define DIRECT_BITSTREAM_SUPPORTED 4
+
+static jint convertAudioDirectModeFromNative(audio_direct_mode_t directMode) {
+    jint result = DIRECT_NOT_SUPPORTED;
+    if ((directMode | AUDIO_DIRECT_OFFLOAD_SUPPORTED) != AUDIO_DIRECT_NOT_SUPPORTED) {
+        result |= DIRECT_OFFLOAD_SUPPORTED;
+    }
+    if ((directMode | AUDIO_DIRECT_OFFLOAD_GAPLESS_SUPPORTED) != AUDIO_DIRECT_NOT_SUPPORTED) {
+        result |= DIRECT_OFFLOAD_GAPLESS_SUPPORTED;
+    }
+    if ((directMode | AUDIO_DIRECT_BITSTREAM_SUPPORTED) != AUDIO_DIRECT_NOT_SUPPORTED) {
+        result |= DIRECT_BITSTREAM_SUPPORTED;
+    }
+    return result;
+}
+
+static jint android_media_AudioSystem_getDirectPlaybackSupport(JNIEnv *env, jobject thiz,
+                                                               jobject jFormat, jobject jaa) {
+    JNIAudioAttributeHelper::UniqueAaPtr paa = JNIAudioAttributeHelper::makeUnique();
+    jint jStatus = JNIAudioAttributeHelper::nativeFromJava(env, jaa, paa.get());
+    if (jStatus != (jint)AUDIO_JAVA_SUCCESS) {
+        return AUDIO_DIRECT_NOT_SUPPORTED;
+    }
+
+    audio_config_t nConfig;
+    javaAudioFormatToNativeAudioConfig(env, &nConfig, jFormat, false /*isInput*/);
+
+    audio_direct_mode_t directMode;
+    status_t status = AudioSystem::getDirectPlaybackSupport(paa.get(), &nConfig, &directMode);
+    if (status != NO_ERROR) {
+        ALOGW("%s native returned error %d", __func__, status);
+        return AUDIO_DIRECT_NOT_SUPPORTED;
+    }
+    return convertAudioDirectModeFromNative(directMode);
+}
+
 // ----------------------------------------------------------------------------
 
 static const JNINativeMethod gMethods[] =
@@ -2917,7 +2957,10 @@
          {"canBeSpatialized",
           "(Landroid/media/AudioAttributes;Landroid/media/AudioFormat;"
           "[Landroid/media/AudioDeviceAttributes;)Z",
-          (void *)android_media_AudioSystem_canBeSpatialized}};
+          (void *)android_media_AudioSystem_canBeSpatialized},
+         {"getDirectPlaybackSupport",
+          "(Landroid/media/AudioFormat;Landroid/media/AudioAttributes;)I",
+          (void *)android_media_AudioSystem_getDirectPlaybackSupport}};
 
 static const JNINativeMethod gEventHandlerMethods[] = {
     {"native_setup",
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index faae9a5..0e042dd 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -2398,6 +2398,77 @@
     }
 
     //====================================================================
+    // Direct playback query
+
+    /** Return value for {@link #getDirectPlaybackSupport(AudioFormat, AudioAttributes)}:
+        direct playback not supported. */
+    public static final int DIRECT_PLAYBACK_NOT_SUPPORTED = AudioSystem.DIRECT_NOT_SUPPORTED;
+    /** Return value for {@link #getDirectPlaybackSupport(AudioFormat, AudioAttributes)}:
+        direct offload playback supported. Compressed offload is a variant of direct playback.
+        It is the feature that allows audio processing tasks to be done on the Android device but
+        not on the application processor, instead, it is handled by dedicated hardware such as audio
+        DSPs. That will allow the application processor to be idle as much as possible, which is
+        good for power saving. Compressed offload playback supports
+        {@link AudioTrack.StreamEventCallback} for event notifications. */
+    public static final int DIRECT_PLAYBACK_OFFLOAD_SUPPORTED =
+            AudioSystem.DIRECT_OFFLOAD_SUPPORTED;
+    /** Return value for {@link #getDirectPlaybackSupport(AudioFormat, AudioAttributes)}:
+        direct offload playback supported with gapless transitions. Compressed offload is a variant
+        of direct playback. It is the feature that allows audio processing tasks to be done on the
+        Android device but not on the application processor, instead, it is handled by dedicated
+        hardware such as audio DSPs. That will allow the application processor to be idle as much as
+        possible, which is good for power saving. Compressed offload playback supports
+        {@link AudioTrack.StreamEventCallback} for event notifications. Gapless transitions
+        indicates the ability to play consecutive audio tracks without an audio silence in
+        between. */
+    public static final int DIRECT_PLAYBACK_OFFLOAD_GAPLESS_SUPPORTED =
+            AudioSystem.DIRECT_OFFLOAD_GAPLESS_SUPPORTED;
+    /** Return value for {@link #getDirectPlaybackSupport(AudioFormat, AudioAttributes)}:
+        direct playback supported. This value covers direct playback that is bitstream pass-through
+        such as compressed pass-through. */
+    public static final int DIRECT_PLAYBACK_BITSTREAM_SUPPORTED =
+            AudioSystem.DIRECT_BITSTREAM_SUPPORTED;
+
+    /** @hide */
+    @IntDef(flag = true, prefix = "DIRECT_PLAYBACK_", value = {
+            DIRECT_PLAYBACK_NOT_SUPPORTED,
+            DIRECT_PLAYBACK_OFFLOAD_SUPPORTED,
+            DIRECT_PLAYBACK_OFFLOAD_GAPLESS_SUPPORTED,
+            DIRECT_PLAYBACK_BITSTREAM_SUPPORTED}
+    )
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface AudioDirectPlaybackMode {}
+
+    /**
+     * Returns a bitfield representing the different forms of direct playback currently available
+     * for a given audio format.
+     * <p>Direct playback means that the audio stream is not altered by the framework. The audio
+     * stream will not be resampled, volume scaled, downmixed or mixed with other content by
+     * the framework. But it may be wrapped in a higher level protocol such as IEC61937 for
+     * passthrough.
+     * <p>Checking for direct support can help the app select the representation of audio content
+     * that most closely matches the capabilities of the device and peripherals (e.g. A/V receiver)
+     * connected to it. Note that the provided stream can still be re-encoded or mixed with other
+     * streams, if needed.
+     * @param format the audio format (codec, sample rate, channels) being checked.
+     * @param attributes the {@link AudioAttributes} to be used for playback
+     * @return the direct playback mode available with given format and attributes. The returned
+     *         value will be {@link #DIRECT_PLAYBACK_NOT_SUPPORTED} or a combination of
+     *         {@link #DIRECT_PLAYBACK_OFFLOAD_SUPPORTED},
+     *         {@link #DIRECT_PLAYBACK_OFFLOAD_GAPLESS_SUPPORTED} and
+     *         {@link #DIRECT_PLAYBACK_BITSTREAM_SUPPORTED}. Note that if
+     *         {@link #DIRECT_PLAYBACK_OFFLOAD_GAPLESS_SUPPORTED} is present in the returned value,
+     *         then {@link #DIRECT_PLAYBACK_OFFLOAD_SUPPORTED} will be too.
+     */
+    @AudioDirectPlaybackMode
+    public static int getDirectPlaybackSupport(@NonNull AudioFormat format,
+                                               @NonNull AudioAttributes attributes) {
+        Objects.requireNonNull(format);
+        Objects.requireNonNull(attributes);
+        return AudioSystem.getDirectPlaybackSupport(format, attributes);
+    }
+
+    //====================================================================
     // Offload query
     /**
      * Returns whether offloaded playback of an audio format is supported on the device.
@@ -2457,7 +2528,9 @@
      *         {@link #PLAYBACK_OFFLOAD_SUPPORTED} if offload playback is supported or
      *         {@link #PLAYBACK_OFFLOAD_GAPLESS_SUPPORTED} if gapless transitions are
      *         also supported.
+     * @deprecated Use {@link #getDirectPlaybackSupport(AudioFormat, AudioAttributes)} instead
      */
+    @Deprecated
     @AudioOffloadMode
     public static int getPlaybackOffloadSupport(@NonNull AudioFormat format,
             @NonNull AudioAttributes attributes) {
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index cc37c38..75dad1e 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -1768,13 +1768,34 @@
 
     /**
      * @hide
+     * Direct playback modes supported by audio HAL implementation.
+     */
+    public static final int DIRECT_NOT_SUPPORTED = 0;
+    public static final int DIRECT_OFFLOAD_SUPPORTED = 1;
+    public static final int DIRECT_OFFLOAD_GAPLESS_SUPPORTED = 3;
+    public static final int DIRECT_BITSTREAM_SUPPORTED = 4;
+
+    /**
+     * @hide
      * Compressed audio offload decoding modes supported by audio HAL implementation.
      * Keep in sync with system/media/include/media/audio.h.
      */
-    public static final int OFFLOAD_NOT_SUPPORTED = 0;
-    public static final int OFFLOAD_SUPPORTED = 1;
+    public static final int OFFLOAD_NOT_SUPPORTED = DIRECT_NOT_SUPPORTED;
+    public static final int OFFLOAD_SUPPORTED = DIRECT_OFFLOAD_SUPPORTED;
     public static final int OFFLOAD_GAPLESS_SUPPORTED = 2;
 
+    /**
+     * @hide
+     * Returns how direct playback of an audio format is currently available on the device.
+     * @param format the audio format (codec, sample rate, channels) being checked.
+     * @param attributes the {@link AudioAttributes} to be used for playback
+     * @return the direct playback mode available with given format and attributes. Any combination
+     *         of {@link #DIRECT_NOT_SUPPORTED}, {@link #DIRECT_OFFLOAD_SUPPORTED},
+     *         {@link #DIRECT_OFFLOAD_GAPLESS_SUPPORTED} and {@link #DIRECT_BITSTREAM_SUPPORTED}.
+     */
+    public static native int getDirectPlaybackSupport(
+            @NonNull AudioFormat format, @NonNull AudioAttributes attributes);
+
     static int getOffloadSupport(@NonNull AudioFormat format, @NonNull AudioAttributes attr) {
         return native_get_offload_support(format.getEncoding(), format.getSampleRate(),
                 format.getChannelMask(), format.getChannelIndexMask(),
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 0450a80..9972e2e 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -1275,8 +1275,8 @@
                     throw new UnsupportedOperationException(
                             "Offload and low latency modes are incompatible");
                 }
-                if (AudioSystem.getOffloadSupport(mFormat, mAttributes)
-                        == AudioSystem.OFFLOAD_NOT_SUPPORTED) {
+                if (AudioSystem.getDirectPlaybackSupport(mFormat, mAttributes)
+                        == AudioSystem.DIRECT_NOT_SUPPORTED) {
                     throw new UnsupportedOperationException(
                             "Cannot create AudioTrack, offload format / attributes not supported");
                 }
@@ -1438,7 +1438,10 @@
      *   the audio data.
      * @param attributes a non-null {@link AudioAttributes} instance.
      * @return true if the given audio format can be played directly.
+     * @deprecated Use {@link AudioManager#getDirectPlaybackSupport(AudioFormat, AudioAttributes)}
+     *             instead.
      */
+    @Deprecated
     public static boolean isDirectPlaybackSupported(@NonNull AudioFormat format,
             @NonNull AudioAttributes attributes) {
         if (format == null) {