Clean up audio resources when VirtualDevice is closed
Limits only one VirtualAudioDevice can be created per VirtualDevice. When VirtualDevice is closed, the associated VirtualAudioDevice and VirtualAudioController will be closed as well. Bug: 218751752 Test: atest FrameworksServicesTests:com.android.server.companion.virtual Change-Id: I6aa8b8af8b526fb52bbb4b23305f28182012803d
This commit is contained in:
@@ -148,6 +148,8 @@ public final class VirtualDeviceManager {
|
||||
}
|
||||
}
|
||||
};
|
||||
@Nullable
|
||||
private VirtualAudioDevice mVirtualAudioDevice;
|
||||
|
||||
private VirtualDevice(
|
||||
IVirtualDeviceManager service,
|
||||
@@ -255,8 +257,8 @@ public final class VirtualDeviceManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the virtual device, stopping and tearing down any virtual displays,
|
||||
* audio policies, and event injection that's currently in progress.
|
||||
* Closes the virtual device, stopping and tearing down any virtual displays, associated
|
||||
* virtual audio device, and event injection that's currently in progress.
|
||||
*/
|
||||
@RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
|
||||
public void close() {
|
||||
@@ -265,6 +267,10 @@ public final class VirtualDeviceManager {
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
if (mVirtualAudioDevice != null) {
|
||||
mVirtualAudioDevice.close();
|
||||
mVirtualAudioDevice = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -351,8 +357,10 @@ public final class VirtualDeviceManager {
|
||||
* Creates a VirtualAudioDevice, capable of recording audio emanating from this device,
|
||||
* or injecting audio from another device.
|
||||
*
|
||||
* <p>Note: This object does not support capturing privileged playback, such as voice call
|
||||
* audio.
|
||||
* <p>Note: One {@link VirtualDevice} can only create one {@link VirtualAudioDevice}, so
|
||||
* calling this method multiple times will return the same instance. When
|
||||
* {@link VirtualDevice#close()} is called, the associated {@link VirtualAudioDevice} will
|
||||
* also be closed automatically.
|
||||
*
|
||||
* @param display The target virtual display to capture from and inject into.
|
||||
* @param executor The {@link Executor} object for the thread on which to execute
|
||||
@@ -368,7 +376,11 @@ public final class VirtualDeviceManager {
|
||||
@NonNull VirtualDisplay display,
|
||||
@Nullable Executor executor,
|
||||
@Nullable AudioConfigurationChangeCallback callback) {
|
||||
return new VirtualAudioDevice(mContext, mVirtualDevice, display, executor, callback);
|
||||
if (mVirtualAudioDevice == null) {
|
||||
mVirtualAudioDevice = new VirtualAudioDevice(
|
||||
mContext, mVirtualDevice, display, executor, callback);
|
||||
}
|
||||
return mVirtualAudioDevice;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -60,7 +60,6 @@ final class UserRestrictionsDetector extends BroadcastReceiver {
|
||||
/** Registers user restrictions change. */
|
||||
void register(@NonNull UserRestrictionsCallback callback) {
|
||||
mUserRestrictionsCallback = callback;
|
||||
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(UserManager.ACTION_USER_RESTRICTIONS_CHANGED);
|
||||
mContext.registerReceiver(/* receiver= */ this, filter);
|
||||
@@ -73,8 +72,10 @@ final class UserRestrictionsDetector extends BroadcastReceiver {
|
||||
|
||||
/** Unregisters user restrictions change. */
|
||||
void unregister() {
|
||||
mUserRestrictionsCallback = null;
|
||||
mContext.unregisterReceiver(/* receiver= */ this);
|
||||
if (mUserRestrictionsCallback != null) {
|
||||
mUserRestrictionsCallback = null;
|
||||
mContext.unregisterReceiver(/* receiver= */ this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -24,6 +24,7 @@ import android.companion.virtual.IVirtualDevice;
|
||||
import android.content.Context;
|
||||
import android.hardware.display.VirtualDisplay;
|
||||
import android.media.AudioFormat;
|
||||
import android.media.AudioManager;
|
||||
import android.media.AudioPlaybackConfiguration;
|
||||
import android.media.AudioRecordingConfiguration;
|
||||
import android.os.RemoteException;
|
||||
@@ -96,7 +97,7 @@ public final class VirtualAudioDevice implements Closeable {
|
||||
|
||||
if (mOngoingSession != null && mOngoingSession.getAudioInjection() != null) {
|
||||
throw new IllegalStateException("Cannot start an audio injection while a session is "
|
||||
+ "ongoing. Call close() on this device first to end the previous injection.");
|
||||
+ "ongoing. Call close() on this device first to end the previous session.");
|
||||
}
|
||||
if (mOngoingSession == null) {
|
||||
mOngoingSession = new VirtualAudioSession(mContext, mCallback, mExecutor);
|
||||
@@ -114,6 +115,9 @@ public final class VirtualAudioDevice implements Closeable {
|
||||
/**
|
||||
* Begins recording audio emanating from this device.
|
||||
*
|
||||
* <p>Note: This method does not support capturing privileged playback, which means the
|
||||
* application can opt out of capturing by {@link AudioManager#setAllowedCapturePolicy(int)}.
|
||||
*
|
||||
* @return An {@link AudioCapture} containing the recorded audio.
|
||||
*/
|
||||
@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
|
||||
@@ -150,6 +154,7 @@ public final class VirtualAudioDevice implements Closeable {
|
||||
return mOngoingSession != null ? mOngoingSession.getAudioInjection() : null;
|
||||
}
|
||||
|
||||
/** Stops audio capture and injection then releases all the resources */
|
||||
@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
@@ -120,7 +120,7 @@ public final class VirtualAudioSession extends IAudioSessionCallback.Stub implem
|
||||
@NonNull
|
||||
public AudioInjection startAudioInjection(@NonNull AudioFormat injectionFormat) {
|
||||
Objects.requireNonNull(injectionFormat, "injectionFormat must not be null");
|
||||
mUserRestrictionsDetector.register(/* callback= */ this);
|
||||
|
||||
synchronized (mLock) {
|
||||
if (mAudioInjection != null) {
|
||||
throw new IllegalStateException(
|
||||
@@ -130,6 +130,8 @@ public final class VirtualAudioSession extends IAudioSessionCallback.Stub implem
|
||||
mInjectionFormat = injectionFormat;
|
||||
mAudioInjection = new AudioInjection();
|
||||
mAudioInjection.play();
|
||||
|
||||
mUserRestrictionsDetector.register(/* callback= */ this);
|
||||
mAudioInjection.setSilent(mUserRestrictionsDetector.isUnmuteMicrophoneDisallowed());
|
||||
return mAudioInjection;
|
||||
}
|
||||
|
||||
@@ -235,6 +235,10 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
|
||||
});
|
||||
mPerDisplayWakelocks.clear();
|
||||
}
|
||||
if (mVirtualAudioController != null) {
|
||||
mVirtualAudioController.stopListening();
|
||||
mVirtualAudioController = null;
|
||||
}
|
||||
}
|
||||
mListener.onClose(mAssociationInfo.getId());
|
||||
mAppToken.unlinkToDeath(this, 0);
|
||||
|
||||
@@ -360,6 +360,16 @@ public class VirtualDeviceManagerServiceTest {
|
||||
assertThat(mDeviceImpl.getVirtualAudioControllerForTesting()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void close_cleanVirtualAudioController() {
|
||||
mDeviceImpl.onVirtualDisplayCreatedLocked(DISPLAY_ID);
|
||||
mDeviceImpl.onAudioSessionStarting(DISPLAY_ID, mCallback);
|
||||
|
||||
mDeviceImpl.close();
|
||||
|
||||
assertThat(mDeviceImpl.getVirtualAudioControllerForTesting()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sendKeyEvent_noFd() {
|
||||
assertThrows(
|
||||
|
||||
Reference in New Issue
Block a user