System apps are not allowed to record better then 16kHz mono
This privacy requirement was documented but was not implemented. Test: atest android.media.cts.AudioPlaybackCaptureTest#testCaptureMediaUsage Bug: 129948989 Change-Id: I256e6ed1965263416a893393d5462ec73099751e Signed-off-by: Kevin Rocard <krocard@google.com>
This commit is contained in:
@@ -400,7 +400,7 @@ public final class AudioAttributes implements Parcelable {
|
||||
/**
|
||||
* Indicates that the audio may be captured by any app.
|
||||
*
|
||||
* For privacy, the following usages can not be recorded: VOICE_COMMUNICATION*,
|
||||
* For privacy, the following usages cannot be recorded: VOICE_COMMUNICATION*,
|
||||
* USAGE_NOTIFICATION*, USAGE_ASSISTANCE* and USAGE_ASSISTANT.
|
||||
*
|
||||
* On {@link android.os.Build.VERSION_CODES#Q}, this means only {@link #USAGE_UNKNOWN},
|
||||
@@ -413,11 +413,11 @@ public final class AudioAttributes implements Parcelable {
|
||||
/**
|
||||
* Indicates that the audio may only be captured by system apps.
|
||||
*
|
||||
* System apps can capture for many purposes like accessibility, user guidance...
|
||||
* System apps can capture for many purposes like accessibility, live captions, user guidance...
|
||||
* but abide to the following restrictions:
|
||||
* - the audio can not leave the device
|
||||
* - the audio can not be passed to a third party app
|
||||
* - the audio can not be recorded at a higher quality then 16kHz 16bit mono
|
||||
* - the audio cannot leave the device
|
||||
* - the audio cannot be passed to a third party app
|
||||
* - the audio cannot be recorded at a higher quality than 16kHz 16bit mono
|
||||
*
|
||||
* See {@link Builder#setAllowedCapturePolicy}.
|
||||
*/
|
||||
|
||||
@@ -91,6 +91,20 @@ public class AudioMix {
|
||||
*/
|
||||
public static final int ROUTE_FLAG_LOOP_BACK = 0x1 << 1;
|
||||
|
||||
/**
|
||||
* An audio mix behavior where the targeted audio is played unaffected but a copy is
|
||||
* accessible for capture through {@link AudioRecord}.
|
||||
*
|
||||
* Only capture of playback is supported, not capture of capture.
|
||||
* Use concurrent capture instead to capture what is captured by other apps.
|
||||
*
|
||||
* The captured audio is an approximation of the played audio.
|
||||
* Effects and volume are not applied, and track are mixed with different delay then in the HAL.
|
||||
* As a result, this API is not suitable for echo cancelling.
|
||||
* @hide
|
||||
*/
|
||||
public static final int ROUTE_FLAG_LOOP_BACK_RENDER = ROUTE_FLAG_LOOP_BACK | ROUTE_FLAG_RENDER;
|
||||
|
||||
private static final int ROUTE_FLAG_SUPPORTED = ROUTE_FLAG_RENDER | ROUTE_FLAG_LOOP_BACK;
|
||||
|
||||
// MIX_TYPE_* values to keep in sync with frameworks/av/include/media/AudioPolicy.h
|
||||
@@ -125,6 +139,15 @@ public class AudioMix {
|
||||
*/
|
||||
public static final int MIX_STATE_MIXING = 1;
|
||||
|
||||
/** Maximum sampling rate for privileged playback capture*/
|
||||
private static final int PRIVILEDGED_CAPTURE_MAX_SAMPLE_RATE = 16000;
|
||||
|
||||
/** Maximum channel number for privileged playback capture*/
|
||||
private static final int PRIVILEDGED_CAPTURE_MAX_CHANNEL_NUMBER = 1;
|
||||
|
||||
/** Maximum channel number for privileged playback capture*/
|
||||
private static final int PRIVILEDGED_CAPTURE_MAX_BYTES_PER_SAMPLE = 2;
|
||||
|
||||
/**
|
||||
* The current mixing state.
|
||||
* @return one of {@link #MIX_STATE_DISABLED}, {@link #MIX_STATE_IDLE},
|
||||
@@ -140,7 +163,8 @@ public class AudioMix {
|
||||
return mRouteFlags;
|
||||
}
|
||||
|
||||
AudioFormat getFormat() {
|
||||
/** @hide */
|
||||
public AudioFormat getFormat() {
|
||||
return mFormat;
|
||||
}
|
||||
|
||||
@@ -182,6 +206,31 @@ public class AudioMix {
|
||||
return true;
|
||||
}
|
||||
|
||||
/** @return an error string if the format would not allow Privileged playbackCapture
|
||||
* null otherwise
|
||||
* @hide */
|
||||
public static String canBeUsedForPrivilegedCapture(AudioFormat format) {
|
||||
int sampleRate = format.getSampleRate();
|
||||
if (sampleRate > PRIVILEDGED_CAPTURE_MAX_SAMPLE_RATE || sampleRate <= 0) {
|
||||
return "Privileged audio capture sample rate " + sampleRate
|
||||
+ " can not be over " + PRIVILEDGED_CAPTURE_MAX_SAMPLE_RATE + "kHz";
|
||||
}
|
||||
int channelCount = format.getChannelCount();
|
||||
if (channelCount > PRIVILEDGED_CAPTURE_MAX_CHANNEL_NUMBER || channelCount <= 0) {
|
||||
return "Privileged audio capture channel count " + channelCount + " can not be over "
|
||||
+ PRIVILEDGED_CAPTURE_MAX_CHANNEL_NUMBER;
|
||||
}
|
||||
int encoding = format.getEncoding();
|
||||
if (!format.isPublicEncoding(encoding) || !format.isEncodingLinearPcm(encoding)) {
|
||||
return "Privileged audio capture encoding " + encoding + "is not linear";
|
||||
}
|
||||
if (format.getBytesPerSample(encoding) > PRIVILEDGED_CAPTURE_MAX_BYTES_PER_SAMPLE) {
|
||||
return "Privileged audio capture encoding " + encoding + " can not be over "
|
||||
+ PRIVILEDGED_CAPTURE_MAX_BYTES_PER_SAMPLE + " bytes per sample";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
@@ -390,6 +439,12 @@ public class AudioMix {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mRule.allowPrivilegedPlaybackCapture()) {
|
||||
String error = AudioMix.canBeUsedForPrivilegedCapture(mFormat);
|
||||
if (error != null) {
|
||||
throw new IllegalArgumentException(error);
|
||||
}
|
||||
}
|
||||
return new AudioMix(mRule, mFormat, mRouteFlags, mCallbackFlags, mDeviceSystemType,
|
||||
mDeviceAddress);
|
||||
}
|
||||
|
||||
@@ -365,6 +365,10 @@ public class AudioMixingRule {
|
||||
/**
|
||||
* Set if the audio of app that opted out of audio playback capture should be captured.
|
||||
*
|
||||
* Caller of this method with <code>true</code>, MUST abide to the restriction listed in
|
||||
* {@link ALLOW_CAPTURE_BY_SYSTEM}, including but not limited to the captured audio
|
||||
* can not leave the capturing app, and the quality is limited to 16k mono.
|
||||
*
|
||||
* The permission {@link CAPTURE_AUDIO_OUTPUT} or {@link CAPTURE_MEDIA_OUTPUT} is needed
|
||||
* to ignore the opt-out.
|
||||
*
|
||||
|
||||
@@ -6761,6 +6761,12 @@ public class AudioService extends IAudioService.Stub
|
||||
if (mix.getRule().allowPrivilegedPlaybackCapture()) {
|
||||
// then it must have CAPTURE_MEDIA_OUTPUT or CAPTURE_AUDIO_OUTPUT permission
|
||||
requireCaptureAudioOrMediaOutputPerm |= true;
|
||||
// and its format must be low quality enough
|
||||
String error = mix.canBeUsedForPrivilegedCapture(mix.getFormat());
|
||||
if (error != null) {
|
||||
Log.e(TAG, error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// If mix is RENDER|LOOPBACK, then an audio MediaProjection is enough
|
||||
|
||||
Reference in New Issue
Block a user