Merge "Delegate playSoundEffect calls to VDM for virtual context"

This commit is contained in:
Ján Sebechlebský
2022-12-21 09:19:35 +00:00
committed by Android (Google) Code Review
3 changed files with 167 additions and 2 deletions

View File

@@ -57,6 +57,7 @@ import android.hardware.input.VirtualNavigationTouchpad;
import android.hardware.input.VirtualNavigationTouchpadConfig;
import android.hardware.input.VirtualTouchscreen;
import android.hardware.input.VirtualTouchscreenConfig;
import android.media.AudioManager;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
@@ -302,6 +303,22 @@ public final class VirtualDeviceManager {
}
}
/**
* Requests sound effect to be played on virtual device.
*
* @see android.media.AudioManager#playSoundEffect(int)
*
* @param deviceId - id of the virtual audio device
* @param effectType the type of sound effect
* @hide
*/
public void playSoundEffect(int deviceId, @AudioManager.SystemSoundEffect int effectType) {
//TODO - handle requests to play sound effects by custom callbacks or SoundPool asociated
// with device session id.
// For now, this is intentionally left empty and effectively disables sound effects for
// virtual devices with custom device audio policy.
}
/**
* A virtual device has its own virtual display, audio output, microphone, and camera etc. The
* creator of a virtual device can take the output from the virtual display and stream it over

View File

@@ -16,6 +16,10 @@
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 android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.IntRange;
@@ -34,6 +38,7 @@ import android.app.compat.CompatChanges;
import android.bluetooth.BluetoothCodecConfig;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothLeAudioCodecConfig;
import android.companion.virtual.VirtualDeviceManager;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
import android.compat.annotation.UnsupportedAppUsage;
@@ -100,6 +105,7 @@ public class AudioManager {
private Context mOriginalContext;
private Context mApplicationContext;
private @Nullable VirtualDeviceManager mVirtualDeviceManager; // Lazy initialized.
private long mVolumeKeyUpTime;
private static final String TAG = "AudioManager";
private static final boolean DEBUG = false;
@@ -858,6 +864,14 @@ public class AudioManager {
return sService;
}
private VirtualDeviceManager getVirtualDeviceManager() {
if (mVirtualDeviceManager != null) {
return mVirtualDeviceManager;
}
mVirtualDeviceManager = getContext().getSystemService(VirtualDeviceManager.class);
return mVirtualDeviceManager;
}
/**
* Sends a simulated key event for a media button.
* To simulate a key press, you must first send a KeyEvent built with a
@@ -3635,11 +3649,15 @@ public class AudioManager {
* whether sounds are heard or not.
* @hide
*/
public void playSoundEffect(@SystemSoundEffect int effectType, int userId) {
public void playSoundEffect(@SystemSoundEffect int effectType, int userId) {
if (effectType < 0 || effectType >= NUM_SOUND_EFFECTS) {
return;
}
if (delegateSoundEffectToVdm(effectType)) {
return;
}
final IAudioService service = getService();
try {
service.playSoundEffect(effectType, userId);
@@ -3657,11 +3675,15 @@ public class AudioManager {
* NOTE: This version is for applications that have their own
* settings panel for enabling and controlling volume.
*/
public void playSoundEffect(@SystemSoundEffect int effectType, float volume) {
public void playSoundEffect(@SystemSoundEffect int effectType, float volume) {
if (effectType < 0 || effectType >= NUM_SOUND_EFFECTS) {
return;
}
if (delegateSoundEffectToVdm(effectType)) {
return;
}
final IAudioService service = getService();
try {
service.playSoundEffectVolume(effectType, volume);
@@ -3670,6 +3692,28 @@ public class AudioManager {
}
}
/**
* Checks whether this {@link AudioManager} instance is asociated with {@link VirtualDevice}
* configured with custom device policy for audio. If there is such device, request to play
* sound effect is forwarded to {@link VirtualDeviceManager}.
*
* @param effectType - The type of sound effect.
* @return true if the request was forwarded to {@link VirtualDeviceManager} instance,
* false otherwise.
*/
private boolean delegateSoundEffectToVdm(@SystemSoundEffect int effectType) {
int deviceId = getContext().getDeviceId();
if (deviceId != DEVICE_ID_DEFAULT) {
VirtualDeviceManager vdm = getVirtualDeviceManager();
if (vdm != null && vdm.getDevicePolicy(deviceId, POLICY_TYPE_AUDIO)
!= DEVICE_POLICY_DEFAULT) {
vdm.playSoundEffect(deviceId, effectType);
return true;
}
}
return false;
}
/**
* Load Sound effects.
* This method must be called when sound effects are enabled.

View File

@@ -0,0 +1,104 @@
/*
* 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.FX_KEY_CLICK;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import android.companion.virtual.VirtualDeviceManager;
import android.content.Context;
import android.media.AudioManager;
import android.test.mock.MockContext;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
public class AudioManagerUnitTest {
private static final int TEST_VIRTUAL_DEVICE_ID = 42;
@Test
public void testAudioManager_playSoundWithDefaultDeviceContext() {
VirtualDeviceManager mockVdm = getMockVirtualDeviceManager(TEST_VIRTUAL_DEVICE_ID,
DEVICE_POLICY_CUSTOM);
Context defaultDeviceContext = getVirtualDeviceMockContext(DEVICE_ID_DEFAULT, /*vdm=*/
mockVdm);
AudioManager audioManager = new AudioManager(defaultDeviceContext);
audioManager.playSoundEffect(FX_KEY_CLICK);
// We expect no interactions with VDM when running on default device.
verifyZeroInteractions(mockVdm);
}
@Test
public void testAudioManager_playSoundWithVirtualDeviceContextDefaultPolicy() {
VirtualDeviceManager mockVdm = getMockVirtualDeviceManager(TEST_VIRTUAL_DEVICE_ID,
DEVICE_POLICY_DEFAULT);
Context defaultDeviceContext = getVirtualDeviceMockContext(TEST_VIRTUAL_DEVICE_ID, /*vdm=*/
mockVdm);
AudioManager audioManager = new AudioManager(defaultDeviceContext);
audioManager.playSoundEffect(FX_KEY_CLICK);
// We expect playback not to be delegated to VDM because of default device policy for audio.
verify(mockVdm, never()).playSoundEffect(anyInt(), anyInt());
}
@Test
public void testAudioManager_playSoundWithVirtualDeviceContextCustomPolicy() {
VirtualDeviceManager mockVdm = getMockVirtualDeviceManager(TEST_VIRTUAL_DEVICE_ID,
DEVICE_POLICY_CUSTOM);
Context defaultDeviceContext = getVirtualDeviceMockContext(TEST_VIRTUAL_DEVICE_ID, /*vdm=*/
mockVdm);
AudioManager audioManager = new AudioManager(defaultDeviceContext);
audioManager.playSoundEffect(FX_KEY_CLICK);
// We expect playback to be delegated to VDM because of custom device policy for audio.
verify(mockVdm, times(1)).playSoundEffect(TEST_VIRTUAL_DEVICE_ID, FX_KEY_CLICK);
}
private static Context getVirtualDeviceMockContext(int deviceId, VirtualDeviceManager vdm) {
MockContext mockContext = mock(MockContext.class);
when(mockContext.getDeviceId()).thenReturn(deviceId);
when(mockContext.getSystemService(VirtualDeviceManager.class)).thenReturn(vdm);
return mockContext;
}
private static VirtualDeviceManager getMockVirtualDeviceManager(
int deviceId, int audioDevicePolicy) {
VirtualDeviceManager vdmMock = mock(VirtualDeviceManager.class);
when(vdmMock.getDevicePolicy(anyInt(), anyInt())).thenReturn(DEVICE_POLICY_DEFAULT);
when(vdmMock.getDevicePolicy(deviceId, POLICY_TYPE_AUDIO)).thenReturn(audioDevicePolicy);
return vdmMock;
}
}