From 1c3408fc6d63f8cb87b06d4d8c656067cdcb227e Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Tue, 9 Nov 2021 12:09:54 +0100 Subject: [PATCH] audio: add call redirection audio modes Add two audio modes corresponding to a call redirection scenario: - AudioManager.MODE_CALL_REDIRECT: PSTN call redirection - AudioManager.MODE_COMMUNICATION_REDIRECT: VoIP call redirection When in call redirect mode, call audio uplink and downlink are not routed to regular audio sinks (e.g. earpiece) or sources (e.g. built in mic) but disconnected. A privileged app can use system APIs to inject and extract call audio and redirect the call to another device. All other media use cases operate as if no call was active. Bug: 189472651 Test: make Change-Id: I3b1a616a72b1f7c97e0e5bf7c9f76c40a28876f1 --- core/api/current.txt | 2 ++ .../android/media/audio/common/AudioMode.aidl | 4 +++ .../android/media/audio/common/AudioMode.aidl | 2 ++ media/java/android/media/AudioManager.java | 32 ++++++++++++++++++- media/java/android/media/AudioSystem.java | 8 ++++- .../android/server/audio/AudioService.java | 13 +++++--- 6 files changed, 55 insertions(+), 6 deletions(-) diff --git a/core/api/current.txt b/core/api/current.txt index 5656c31c330af..7411d776d15ba 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -20732,7 +20732,9 @@ package android.media { 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_REDIRECT = 5; // 0x5 field public static final int MODE_CALL_SCREENING = 4; // 0x4 + field public static final int MODE_COMMUNICATION_REDIRECT = 6; // 0x6 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/media/aidl/android/media/audio/common/AudioMode.aidl b/media/aidl/android/media/audio/common/AudioMode.aidl index cc03813776e86..701bcb60bb27d 100644 --- a/media/aidl/android/media/audio/common/AudioMode.aidl +++ b/media/aidl/android/media/audio/common/AudioMode.aidl @@ -45,4 +45,8 @@ enum AudioMode { IN_COMMUNICATION = 3, /** Call screening in progress. */ CALL_SCREEN = 4, + /** PSTN Call redirection in progress. */ + SYS_RESERVED_CALL_REDIRECT = 5, + /** VoIP Call redirection in progress. */ + SYS_RESERVED_COMMUNICATION_REDIRECT = 6, } diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioMode.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioMode.aidl index 8ae1c10fccf58..8256c1c81b77b 100644 --- a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioMode.aidl +++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioMode.aidl @@ -42,4 +42,6 @@ enum AudioMode { IN_CALL = 2, IN_COMMUNICATION = 3, CALL_SCREEN = 4, + SYS_RESERVED_CALL_REDIRECT = 5, + SYS_RESERVED_COMMUNICATION_REDIRECT = 6, } diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index e3ed703f475d5..c6622238a7921 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -30,8 +30,11 @@ import android.annotation.SystemService; import android.annotation.TestApi; import android.app.NotificationManager; import android.app.PendingIntent; +import android.app.compat.CompatChanges; import android.bluetooth.BluetoothCodecConfig; import android.bluetooth.BluetoothDevice; +import android.compat.annotation.ChangeId; +import android.compat.annotation.EnabledSince; import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; @@ -2839,6 +2842,14 @@ public class AudioManager { } } + /** + * This change id controls use of audio modes for call audio redirection. + * @hide + */ + @ChangeId + @EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU) + public static final long CALL_REDIRECTION_AUDIO_MODES = 189472651L; // buganizer id + /** * Returns the current audio mode. * @@ -2858,6 +2869,12 @@ public class AudioManager { } if (mode == MODE_CALL_SCREENING && sdk <= Build.VERSION_CODES.Q) { mode = MODE_IN_CALL; + } else if (mode == MODE_CALL_REDIRECT + && !CompatChanges.isChangeEnabled(CALL_REDIRECTION_AUDIO_MODES)) { + mode = MODE_IN_CALL; + } else if (mode == MODE_COMMUNICATION_REDIRECT + && !CompatChanges.isChangeEnabled(CALL_REDIRECTION_AUDIO_MODES)) { + mode = MODE_IN_COMMUNICATION; } return mode; } catch (RemoteException e) { @@ -3075,13 +3092,26 @@ public class AudioManager { */ public static final int MODE_CALL_SCREENING = AudioSystem.MODE_CALL_SCREENING; + /** + * A telephony call is established and its audio is being redirected to another device. + */ + public static final int MODE_CALL_REDIRECT = AudioSystem.MODE_CALL_REDIRECT; + + /** + * n audio/video chat or VoIP call is established and its audio is being redirected to another + * device. + */ + public static final int MODE_COMMUNICATION_REDIRECT = AudioSystem.MODE_COMMUNICATION_REDIRECT; + /** @hide */ @IntDef(flag = false, prefix = "MODE_", value = { MODE_NORMAL, MODE_RINGTONE, MODE_IN_CALL, MODE_IN_COMMUNICATION, - MODE_CALL_SCREENING } + MODE_CALL_SCREENING, + MODE_CALL_REDIRECT, + MODE_COMMUNICATION_REDIRECT} ) @Retention(RetentionPolicy.SOURCE) public @interface AudioMode {} diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java index 60c9e1c308b6c..16cb5f437414b 100644 --- a/media/java/android/media/AudioSystem.java +++ b/media/java/android/media/AudioSystem.java @@ -199,7 +199,11 @@ public class AudioSystem /** @hide */ public static final int MODE_CALL_SCREENING = 4; /** @hide */ - public static final int NUM_MODES = 5; + public static final int MODE_CALL_REDIRECT = 5; + /** @hide */ + public static final int MODE_COMMUNICATION_REDIRECT = 6; + /** @hide */ + public static final int NUM_MODES = 7; /** @hide */ public static String modeToString(int mode) { @@ -211,6 +215,8 @@ public class AudioSystem case MODE_NORMAL: return "MODE_NORMAL"; case MODE_RINGTONE: return "MODE_RINGTONE"; case MODE_CALL_SCREENING: return "MODE_CALL_SCREENING"; + case MODE_CALL_REDIRECT: return "MODE_CALL_REDIRECT"; + case MODE_COMMUNICATION_REDIRECT: return "MODE_COMMUNICATION_REDIRECT"; default: return "unknown mode (" + mode + ")"; } } diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 4dd168252b801..18be9a10a37a7 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -4638,12 +4638,13 @@ public class AudioService extends IAudioService.Stub * or recording for VOICE_COMMUNICATION. * or * - It requests a mode different from MODE_IN_COMMUNICATION or MODE_NORMAL + * Note: only privileged apps can request MODE_IN_CALL, MODE_CALL_REDIRECT + * or MODE_COMMUNICATION_REDIRECT. */ public boolean isActive() { return mIsPrivileged || ((mMode == AudioSystem.MODE_IN_COMMUNICATION) && (mRecordingActive || mPlaybackActive)) - || mMode == AudioSystem.MODE_IN_CALL || mMode == AudioSystem.MODE_RINGTONE || mMode == AudioSystem.MODE_CALL_SCREENING; } @@ -4752,9 +4753,13 @@ public class AudioService extends IAudioService.Stub final boolean hasModifyPhoneStatePermission = mContext.checkCallingOrSelfPermission( android.Manifest.permission.MODIFY_PHONE_STATE) == PackageManager.PERMISSION_GRANTED; - if ((mode == AudioSystem.MODE_IN_CALL) && !hasModifyPhoneStatePermission) { - Log.w(TAG, "MODIFY_PHONE_STATE Permission Denial: setMode(MODE_IN_CALL) from pid=" - + pid + ", uid=" + Binder.getCallingUid()); + if ((mode == AudioSystem.MODE_IN_CALL + || mode == AudioSystem.MODE_CALL_REDIRECT + || mode == AudioSystem.MODE_COMMUNICATION_REDIRECT) + && !hasModifyPhoneStatePermission) { + Log.w(TAG, "MODIFY_PHONE_STATE Permission Denial: setMode(" + + AudioSystem.modeToString(mode) + ") from pid=" + pid + + ", uid=" + Binder.getCallingUid()); return; }