Configure session ids of AudioRecord for device-specific context.
Bug: 261698699 Bug: 249777386 Test: atest AudioRecordUnitTest AudioRecordTest Change-Id: I1361d83e902e74a9472a8ff5fe75e01b8430f00c
This commit is contained in:
committed by
Ján Sebechlebský
parent
2d20d1c998
commit
14ca04c361
@@ -16,6 +16,11 @@
|
||||
|
||||
package android.media;
|
||||
|
||||
import static android.companion.virtual.VirtualDeviceManager.DEVICE_ID_DEFAULT;
|
||||
import static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_DEFAULT;
|
||||
import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_AUDIO;
|
||||
import static android.media.AudioManager.AUDIO_SESSION_ID_GENERATE;
|
||||
|
||||
import android.annotation.CallbackExecutor;
|
||||
import android.annotation.FloatRange;
|
||||
import android.annotation.IntDef;
|
||||
@@ -26,6 +31,7 @@ import android.annotation.RequiresPermission;
|
||||
import android.annotation.SystemApi;
|
||||
import android.annotation.TestApi;
|
||||
import android.app.ActivityThread;
|
||||
import android.companion.virtual.VirtualDeviceManager;
|
||||
import android.compat.annotation.UnsupportedAppUsage;
|
||||
import android.content.AttributionSource;
|
||||
import android.content.AttributionSource.ScopedParcelState;
|
||||
@@ -455,7 +461,7 @@ public class AudioRecord implements AudioRouting, MicrophoneDirection,
|
||||
|
||||
int[] sampleRate = new int[] {mSampleRate};
|
||||
int[] session = new int[1];
|
||||
session[0] = sessionId;
|
||||
session[0] = resolveSessionId(context, sessionId);
|
||||
|
||||
//TODO: update native initialization when information about hardware init failure
|
||||
// due to capture device already open is available.
|
||||
@@ -624,15 +630,15 @@ public class AudioRecord implements AudioRouting, MicrophoneDirection,
|
||||
|
||||
/**
|
||||
* Sets the context the record belongs to. This context will be used to pull information,
|
||||
* such as {@link android.content.AttributionSource}, which will be associated with
|
||||
* the AudioRecord. However, the context itself will not be retained by the AudioRecord.
|
||||
* such as {@link android.content.AttributionSource} and device specific session ids,
|
||||
* which will be associated with the {@link AudioRecord} the AudioRecord.
|
||||
* However, the context itself will not be retained by the AudioRecord.
|
||||
* @param context a non-null {@link Context} instance
|
||||
* @return the same Builder instance.
|
||||
*/
|
||||
public @NonNull Builder setContext(@NonNull Context context) {
|
||||
Objects.requireNonNull(context);
|
||||
// keep reference, we only copy the data when building
|
||||
mContext = context;
|
||||
mContext = Objects.requireNonNull(context);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -746,6 +752,9 @@ public class AudioRecord implements AudioRouting, MicrophoneDirection,
|
||||
/**
|
||||
* @hide
|
||||
* To be only used by system components.
|
||||
*
|
||||
* Note, that if there's a device specific session id asociated with the context, explicitly
|
||||
* setting a session id using this method will override it.
|
||||
* @param sessionId ID of audio session the AudioRecord must be attached to, or
|
||||
* {@link AudioManager#AUDIO_SESSION_ID_GENERATE} if the session isn't known at
|
||||
* construction time.
|
||||
@@ -983,6 +992,45 @@ public class AudioRecord implements AudioRouting, MicrophoneDirection,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to resolve session id to be used for AudioRecord initialization.
|
||||
*
|
||||
* This method will assign session id in following way:
|
||||
* 1. Explicitly requested session id has the highest priority, if there is one,
|
||||
* it will be used.
|
||||
* 2. If there's device-specific session id asociated with the provided context,
|
||||
* it will be used.
|
||||
* 3. Otherwise {@link AUDIO_SESSION_ID_GENERATE} is returned.
|
||||
*
|
||||
* @param context {@link Context} to use for extraction of device specific session id.
|
||||
* @param requestedSessionId explicitly requested session id or AUDIO_SESSION_ID_GENERATE.
|
||||
* @return session id to be passed to AudioService for the {@link AudioRecord} instance given
|
||||
* provided {@link Context} instance and explicitly requested session id.
|
||||
*/
|
||||
private static int resolveSessionId(@Nullable Context context, int requestedSessionId) {
|
||||
if (requestedSessionId != AUDIO_SESSION_ID_GENERATE) {
|
||||
// Use explicitly requested session id.
|
||||
return requestedSessionId;
|
||||
}
|
||||
|
||||
if (context == null) {
|
||||
return AUDIO_SESSION_ID_GENERATE;
|
||||
}
|
||||
|
||||
int deviceId = context.getDeviceId();
|
||||
if (deviceId == DEVICE_ID_DEFAULT) {
|
||||
return AUDIO_SESSION_ID_GENERATE;
|
||||
}
|
||||
|
||||
VirtualDeviceManager vdm = context.getSystemService(VirtualDeviceManager.class);
|
||||
if (vdm == null || vdm.getDevicePolicy(deviceId, POLICY_TYPE_AUDIO)
|
||||
== DEVICE_POLICY_DEFAULT) {
|
||||
return AUDIO_SESSION_ID_GENERATE;
|
||||
}
|
||||
|
||||
return vdm.getAudioRecordingSessionId(deviceId);
|
||||
}
|
||||
|
||||
// Convenience method for the constructor's parameter checks.
|
||||
// This, getChannelMaskFromLegacyConfig and audioBuffSizeCheck are where constructor
|
||||
// IllegalArgumentException-s are thrown
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.mediaframeworktest.unit;
|
||||
|
||||
import static android.companion.virtual.VirtualDeviceManager.DEVICE_ID_DEFAULT;
|
||||
import static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_CUSTOM;
|
||||
import static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_DEFAULT;
|
||||
import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_AUDIO;
|
||||
import static android.media.AudioManager.AUDIO_SESSION_ID_GENERATE;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.companion.virtual.VirtualDeviceManager;
|
||||
import android.content.Context;
|
||||
import android.media.AudioFormat;
|
||||
import android.media.AudioManager;
|
||||
import android.media.AudioRecord;
|
||||
import android.media.MediaRecorder;
|
||||
import android.test.mock.MockContext;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class AudioRecordUnitTest {
|
||||
private static final int TEST_SAMPLE_RATE = 44000;
|
||||
private static final int TEST_VIRTUAL_DEVICE_ID = 42;
|
||||
private static final AudioFormat TEST_AUDIO_FORMAT = new AudioFormat.Builder().setSampleRate(
|
||||
TEST_SAMPLE_RATE).setEncoding(AudioFormat.ENCODING_PCM_16BIT).setChannelMask(
|
||||
AudioFormat.CHANNEL_IN_MONO).build();
|
||||
private static final int TEST_BUFFER_SIZE = AudioRecord.getMinBufferSize(
|
||||
TEST_AUDIO_FORMAT.getSampleRate(),
|
||||
TEST_AUDIO_FORMAT.getChannelMask(), TEST_AUDIO_FORMAT.getEncoding());
|
||||
|
||||
@Test
|
||||
public void testBuilderConstructionWithContext_defaultDeviceExplicitSessionId() {
|
||||
Context mockDefaultDeviceContext = getVirtualDeviceMockContext(
|
||||
DEVICE_ID_DEFAULT, /*vdm=*/null);
|
||||
int sessionId = getContext().getSystemService(AudioManager.class).generateAudioSessionId();
|
||||
|
||||
AudioRecord audioRecord = new AudioRecord.Builder().setContext(
|
||||
mockDefaultDeviceContext).setAudioSource(
|
||||
MediaRecorder.AudioSource.DEFAULT).setAudioFormat(
|
||||
TEST_AUDIO_FORMAT).setBufferSizeInBytes(TEST_BUFFER_SIZE).setSessionId(
|
||||
sessionId).build();
|
||||
|
||||
assertEquals(AudioRecord.STATE_INITIALIZED, audioRecord.getState());
|
||||
assertEquals(sessionId, audioRecord.getAudioSessionId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBuilderConstructionWithContext_virtualDeviceDefaultAudioPolicy() {
|
||||
int vdmPlaybackSessionId = getContext().getSystemService(
|
||||
AudioManager.class).generateAudioSessionId();
|
||||
VirtualDeviceManager mockVdm = getMockVirtualDeviceManager(TEST_VIRTUAL_DEVICE_ID,
|
||||
vdmPlaybackSessionId, DEVICE_POLICY_DEFAULT);
|
||||
Context virtualDeviceContext = getVirtualDeviceMockContext(TEST_VIRTUAL_DEVICE_ID, mockVdm);
|
||||
|
||||
AudioRecord audioRecord = new AudioRecord.Builder().setContext(
|
||||
virtualDeviceContext).setAudioSource(
|
||||
MediaRecorder.AudioSource.DEFAULT).setAudioFormat(
|
||||
TEST_AUDIO_FORMAT).setBufferSizeInBytes(TEST_BUFFER_SIZE).build();
|
||||
|
||||
assertEquals(AudioRecord.STATE_INITIALIZED, audioRecord.getState());
|
||||
assertNotEquals(vdmPlaybackSessionId, audioRecord.getAudioSessionId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBuilderConstructionWithContext_virtualDeviceCustomAudioPolicy() {
|
||||
int vdmRecordingSessionId = getContext().getSystemService(
|
||||
AudioManager.class).generateAudioSessionId();
|
||||
VirtualDeviceManager mockVdm = getMockVirtualDeviceManager(TEST_VIRTUAL_DEVICE_ID,
|
||||
vdmRecordingSessionId, DEVICE_POLICY_CUSTOM);
|
||||
Context virtualDeviceContext = getVirtualDeviceMockContext(TEST_VIRTUAL_DEVICE_ID, mockVdm);
|
||||
|
||||
AudioRecord audioRecord = new AudioRecord.Builder().setContext(
|
||||
virtualDeviceContext).setAudioSource(
|
||||
MediaRecorder.AudioSource.DEFAULT).setAudioFormat(
|
||||
TEST_AUDIO_FORMAT).setBufferSizeInBytes(TEST_BUFFER_SIZE).build();
|
||||
|
||||
assertEquals(AudioRecord.STATE_INITIALIZED, audioRecord.getState());
|
||||
assertEquals(vdmRecordingSessionId, audioRecord.getAudioSessionId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBuilderConstructionWithContext_virtualDeviceSetSessionIdOverridesContext() {
|
||||
int vdmRecordingSessionId = getContext().getSystemService(
|
||||
AudioManager.class).generateAudioSessionId();
|
||||
int anotherSessionId = getContext().getSystemService(
|
||||
AudioManager.class).generateAudioSessionId();
|
||||
VirtualDeviceManager mockVdm = getMockVirtualDeviceManager(TEST_VIRTUAL_DEVICE_ID,
|
||||
vdmRecordingSessionId, DEVICE_POLICY_CUSTOM);
|
||||
Context virtualDeviceContext = getVirtualDeviceMockContext(TEST_VIRTUAL_DEVICE_ID, mockVdm);
|
||||
|
||||
AudioRecord audioRecord = new AudioRecord.Builder().setContext(
|
||||
virtualDeviceContext).setAudioSource(
|
||||
MediaRecorder.AudioSource.DEFAULT).setAudioSource(
|
||||
MediaRecorder.AudioSource.DEFAULT).setBufferSizeInBytes(
|
||||
TEST_BUFFER_SIZE).setSessionId(
|
||||
anotherSessionId).build();
|
||||
|
||||
assertEquals(AudioRecord.STATE_INITIALIZED, audioRecord.getState());
|
||||
assertEquals(anotherSessionId, audioRecord.getAudioSessionId());
|
||||
}
|
||||
|
||||
private Context getContext() {
|
||||
return InstrumentationRegistry.getInstrumentation().getContext();
|
||||
}
|
||||
|
||||
private Context getVirtualDeviceMockContext(int deviceId, VirtualDeviceManager vdm) {
|
||||
MockContext mockContext = mock(MockContext.class);
|
||||
when(mockContext.getDeviceId()).thenReturn(deviceId);
|
||||
when(mockContext.getSystemService(VirtualDeviceManager.class)).thenReturn(vdm);
|
||||
when(mockContext.getAttributionSource()).thenReturn(getContext().getAttributionSource());
|
||||
return mockContext;
|
||||
}
|
||||
|
||||
private static VirtualDeviceManager getMockVirtualDeviceManager(int deviceId,
|
||||
int recordingSessionId, int audioDevicePolicy) {
|
||||
VirtualDeviceManager vdmMock = mock(VirtualDeviceManager.class);
|
||||
when(vdmMock.getAudioRecordingSessionId(anyInt())).thenReturn(AUDIO_SESSION_ID_GENERATE);
|
||||
when(vdmMock.getAudioRecordingSessionId(deviceId)).thenReturn(recordingSessionId);
|
||||
when(vdmMock.getDevicePolicy(anyInt(), anyInt())).thenReturn(DEVICE_POLICY_DEFAULT);
|
||||
when(vdmMock.getDevicePolicy(deviceId, POLICY_TYPE_AUDIO)).thenReturn(audioDevicePolicy);
|
||||
return vdmMock;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user