AudioManager: Add call screening audio mode.

Add new audio mode MODE_CALL_SCREENING allowing call screening
to take place while other audio use cases are still active.

Also add API to indicate if the platform supports this audio mode.

Bug: 140384450
Test: make
Change-Id: I056f79580d9c7b4de5eabb6d8d8c917f39162bcd
diff --git a/api/current.txt b/api/current.txt
index f9a2957..81cf7cf 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -23500,6 +23500,7 @@
     method @Deprecated public boolean isBluetoothA2dpOn();
     method public boolean isBluetoothScoAvailableOffCall();
     method public boolean isBluetoothScoOn();
+    method public boolean isCallScreeningModeSupported();
     method public static boolean isHapticPlaybackSupported();
     method public boolean isMicrophoneMute();
     method public boolean isMusicActive();
@@ -23598,6 +23599,7 @@
     field public static final int GET_DEVICES_ALL = 3; // 0x3
     field public static final int GET_DEVICES_INPUTS = 1; // 0x1
     field public static final int GET_DEVICES_OUTPUTS = 2; // 0x2
+    field public static final int MODE_CALL_SCREENING = 4; // 0x4
     field public static final int MODE_CURRENT = -1; // 0xffffffff
     field public static final int MODE_INVALID = -2; // 0xfffffffe
     field public static final int MODE_IN_CALL = 2; // 0x2
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 541b937..01f9d0b0 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -2264,6 +2264,12 @@
     return jStatus;
 }
 
+static jboolean
+android_media_AudioSystem_isCallScreeningModeSupported(JNIEnv *env, jobject thiz)
+{
+    return AudioSystem::isCallScreenModeSupported();
+}
+
 // ----------------------------------------------------------------------------
 
 static const JNINativeMethod gMethods[] = {
@@ -2343,6 +2349,7 @@
     {"setAllowedCapturePolicy", "(II)I", (void *)android_media_AudioSystem_setAllowedCapturePolicy},
     {"setRttEnabled",       "(Z)I",     (void *)android_media_AudioSystem_setRttEnabled},
     {"setAudioHalPids",  "([I)I", (void *)android_media_AudioSystem_setAudioHalPids},
+    {"isCallScreeningModeSupported", "()Z", (void *)android_media_AudioSystem_isCallScreeningModeSupported},
 };
 
 static const JNINativeMethod gEventHandlerMethods[] = {
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 9ad7f2a..d552491 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -1,4 +1,5 @@
 /*
+/*
  * Copyright (C) 2007 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -1931,12 +1932,11 @@
      * application when it places a phone call, as it will cause signals from the radio layer
      * to feed the platform mixer.
      *
-     * @param mode  the requested audio mode ({@link #MODE_NORMAL}, {@link #MODE_RINGTONE},
-     *              {@link #MODE_IN_CALL} or {@link #MODE_IN_COMMUNICATION}).
+     * @param mode  the requested audio mode.
      *              Informs the HAL about the current audio state so that
      *              it can route the audio appropriately.
      */
-    public void setMode(int mode) {
+    public void setMode(@AudioMode int mode) {
         final IAudioService service = getService();
         try {
             service.setMode(mode, mICallBack, mApplicationContext.getOpPackageName());
@@ -1948,14 +1948,47 @@
     /**
      * Returns the current audio mode.
      *
-     * @return      the current audio mode ({@link #MODE_NORMAL}, {@link #MODE_RINGTONE},
-     *              {@link #MODE_IN_CALL} or {@link #MODE_IN_COMMUNICATION}).
-     *              Returns the current current audio state from the HAL.
+     * @return      the current audio mode.
      */
+    @AudioMode
     public int getMode() {
         final IAudioService service = getService();
         try {
-            return service.getMode();
+            int mode = service.getMode();
+            int sdk;
+            try {
+                sdk = getContext().getApplicationInfo().targetSdkVersion;
+            } catch (NullPointerException e) {
+                // some tests don't have a Context
+                sdk = Build.VERSION.SDK_INT;
+            }
+            if (mode == MODE_CALL_SCREENING && sdk <= Build.VERSION_CODES.Q) {
+                mode = MODE_IN_CALL;
+            }
+            return mode;
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+    * Indicates if the platform supports a special call screening and call monitoring mode.
+    * <p>
+    * When this mode is supported, it is possible to perform call screening and monitoring
+    * functions while other use cases like music or movie playback are active.
+    * <p>
+    * Use {@link #setMode(int)} with mode {@link #MODE_CALL_SCREENING} to place the platform in
+    * call screening mode.
+    * <p>
+    * If call screening mode is not supported, setting mode to
+    * MODE_CALL_SCREENING will be ignored and will not change current mode reported by
+    *  {@link #getMode()}.
+    * @return true if call screening mode is supported, false otherwise.
+    */
+    public boolean isCallScreeningModeSupported() {
+        final IAudioService service = getService();
+        try {
+            return service.isCallScreeningModeSupported();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1989,6 +2022,22 @@
      * In communication audio mode. An audio/video chat or VoIP call is established.
      */
     public static final int MODE_IN_COMMUNICATION   = AudioSystem.MODE_IN_COMMUNICATION;
+    /**
+     * Call screening in progress. Call is connected and audio is accessible to call
+     * screening applications but other audio use cases are still possible.
+     */
+    public static final int MODE_CALL_SCREENING     = AudioSystem.MODE_CALL_SCREENING;
+
+    /** @hide */
+    @IntDef(flag = false, prefix = "MODE_", value = {
+            MODE_NORMAL,
+            MODE_RINGTONE,
+            MODE_IN_CALL,
+            MODE_IN_COMMUNICATION,
+            MODE_CALL_SCREENING }
+    )
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface AudioMode {}
 
     /* Routing bits for setRouting/getRouting API */
     /**
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 900572d..a3a8777 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -132,7 +132,8 @@
     public static final int MODE_RINGTONE           = 1;
     public static final int MODE_IN_CALL            = 2;
     public static final int MODE_IN_COMMUNICATION   = 3;
-    public static final int NUM_MODES               = 4;
+    public static final int MODE_CALL_SCREENING     = 4;
+    public static final int NUM_MODES               = 5;
 
     public static String modeToString(int mode) {
         switch (mode) {
@@ -142,6 +143,7 @@
             case MODE_INVALID: return "MODE_INVALID";
             case MODE_NORMAL: return "MODE_NORMAL";
             case MODE_RINGTONE: return "MODE_RINGTONE";
+            case MODE_CALL_SCREENING: return "MODE_CALL_SCREENING";
             default: return "unknown mode (" + mode + ")";
         }
     }
@@ -1130,6 +1132,11 @@
      */
     public static native int setAudioHalPids(int[] pids);
 
+    /**
+     * @see AudioManager#isCallScreeningModeSupported()
+     */
+    public static native boolean isCallScreeningModeSupported();
+
     // Items shared with audio service
 
     /**
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index fc05610..ef451ce 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -263,6 +263,8 @@
 
     boolean hasHapticChannels(in Uri uri);
 
+    boolean isCallScreeningModeSupported();
+
     // WARNING: read warning at top of file, new methods that need to be used by native
     // code via IAudioManager.h need to be added to the top section.
 }
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index d09139c..b81f5e1 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -772,6 +772,8 @@
 
         readAndSetLowRamDevice();
 
+        mIsCallScreeningModeSupported = AudioSystem.isCallScreeningModeSupported();
+
         // Call setRingerModeInt() to apply correct mute
         // state on streams affected by ringer mode.
         mRingerAndZenModeMutedStreams = 0;
@@ -965,6 +967,8 @@
 
         readAndSetLowRamDevice();
 
+        mIsCallScreeningModeSupported = AudioSystem.isCallScreeningModeSupported();
+
         // Restore device connection states, BT state
         mDeviceBroker.onAudioServerDied();
 
@@ -3287,6 +3291,12 @@
             return;
         }
 
+        if (mode == AudioSystem.MODE_CALL_SCREENING && !mIsCallScreeningModeSupported) {
+            Log.w(TAG, "setMode(MODE_CALL_SCREENING) not permitted "
+                    + "when call screening is not supported");
+            return;
+        }
+
         if (mode < AudioSystem.MODE_CURRENT || mode >= AudioSystem.NUM_MODES) {
             return;
         }
@@ -3417,6 +3427,14 @@
         return mMode;
     }
 
+    /** cached value read from audiopolicy manager after initialization. */
+    private boolean mIsCallScreeningModeSupported = false;
+
+    /** @see AudioManager#isCallScreeningModeSupported() */
+    public boolean isCallScreeningModeSupported() {
+        return mIsCallScreeningModeSupported;
+    }
+
     //==========================================================================================
     // Sound Effects
     //==========================================================================================
@@ -6149,6 +6167,7 @@
         pw.print("  mHdmiPlaybackClient="); pw.println(mHdmiPlaybackClient);
         pw.print("  mHdmiTvClient="); pw.println(mHdmiTvClient);
         pw.print("  mHdmiSystemAudioSupported="); pw.println(mHdmiSystemAudioSupported);
+        pw.print("  mIsCallScreeningModeSupported="); pw.println(mIsCallScreeningModeSupported);
 
         dumpAudioPolicies(pw);
         mDynPolicyLogger.dump(pw);