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
This commit is contained in:
Eric Laurent
2021-11-09 12:09:54 +01:00
parent 420679dea6
commit 1c3408fc6d
6 changed files with 55 additions and 6 deletions

View File

@@ -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

View File

@@ -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,
}

View File

@@ -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,
}

View File

@@ -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 {}

View File

@@ -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 + ")";
}
}

View File

@@ -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;
}