Merge "Clean up audio resources when VirtualDevice is closed"

This commit is contained in:
Yuanjia Hsu
2022-02-17 04:20:12 +00:00
committed by Android (Google) Code Review
6 changed files with 44 additions and 10 deletions

View File

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

View File

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

View File

@@ -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() {

View File

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

View File

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

View File

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