From f45b47964d0978904f92f0dca241f1608e148389 Mon Sep 17 00:00:00 2001 From: yuanjiahsu Date: Tue, 8 Feb 2022 14:21:02 +0800 Subject: [PATCH] Audio Injection respect Enterprise Policy Listens to User Restrictions change of DISALLOW_UNMUTE_MICROPHONE to suspend or resume audio injection. Bug: 195654431 Test: manual testing by go/testdpc pass Change-Id: Ifedb89172edb0dc6474e078dea0c96f1c7038c4c --- .../virtual/audio/AudioInjection.java | 13 ++- .../audio/UserRestrictionsDetector.java | 96 +++++++++++++++++++ .../virtual/audio/VirtualAudioSession.java | 18 +++- 3 files changed, 122 insertions(+), 5 deletions(-) create mode 100644 core/java/android/companion/virtual/audio/UserRestrictionsDetector.java diff --git a/core/java/android/companion/virtual/audio/AudioInjection.java b/core/java/android/companion/virtual/audio/AudioInjection.java index 0519075038472..5e8e0a487a2e0 100644 --- a/core/java/android/companion/virtual/audio/AudioInjection.java +++ b/core/java/android/companion/virtual/audio/AudioInjection.java @@ -43,13 +43,20 @@ public final class AudioInjection { private static final String TAG = "AudioInjection"; private final Object mLock = new Object(); - @GuardedBy("mLock") @Nullable private AudioTrack mAudioTrack; - @GuardedBy("mLock") private int mPlayState = PLAYSTATE_STOPPED; + @GuardedBy("mLock") + private boolean mIsSilent; + + /** Sets if the injected microphone sound is silent. */ + void setSilent(boolean isSilent) { + synchronized (mLock) { + mIsSilent = isSilent; + } + } /** * Sets the {@link AudioTrack} to handle audio injection. @@ -86,7 +93,7 @@ public final class AudioInjection { public int write(@NonNull ByteBuffer audioBuffer, int sizeInBytes, int writeMode) { final int sizeWrite; synchronized (mLock) { - if (mAudioTrack != null) { + if (mAudioTrack != null && !mIsSilent) { sizeWrite = mAudioTrack.write(audioBuffer, sizeInBytes, writeMode); } else { sizeWrite = 0; diff --git a/core/java/android/companion/virtual/audio/UserRestrictionsDetector.java b/core/java/android/companion/virtual/audio/UserRestrictionsDetector.java new file mode 100644 index 0000000000000..5c246d3657518 --- /dev/null +++ b/core/java/android/companion/virtual/audio/UserRestrictionsDetector.java @@ -0,0 +1,96 @@ +/* + * 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 android.companion.virtual.audio; + +import android.annotation.NonNull; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Bundle; +import android.os.UserManager; + +import com.android.internal.annotations.GuardedBy; + +/** + * Class to detect the user restrictions change for microphone usage. + */ +final class UserRestrictionsDetector extends BroadcastReceiver { + private static final String TAG = "UserRestrictionsDetector"; + + /** Interface for listening user restrictions change. */ + interface UserRestrictionsCallback { + + /** Notifies when value of {@link UserManager#DISALLOW_UNMUTE_MICROPHONE} is changed. */ + void onMicrophoneRestrictionChanged(boolean isUnmuteMicDisallowed); + } + + private final Context mContext; + private final UserManager mUserManager; + private final Object mLock = new Object(); + @GuardedBy("mLock") + private boolean mIsUnmuteMicDisallowed; + private UserRestrictionsCallback mUserRestrictionsCallback; + + UserRestrictionsDetector(Context context) { + mContext = context; + mUserManager = context.getSystemService(UserManager.class); + } + + /** Returns value of {@link UserManager#DISALLOW_UNMUTE_MICROPHONE}. */ + boolean isUnmuteMicrophoneDisallowed() { + Bundle bundle = mUserManager.getUserRestrictions(); + return bundle.getBoolean(UserManager.DISALLOW_UNMUTE_MICROPHONE); + } + + /** 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); + + synchronized (mLock) { + // Gets initial value. + mIsUnmuteMicDisallowed = isUnmuteMicrophoneDisallowed(); + } + } + + /** Unregisters user restrictions change. */ + void unregister() { + mUserRestrictionsCallback = null; + mContext.unregisterReceiver(/* receiver= */ this); + } + + @Override + public void onReceive(Context context, Intent intent) { + final String action = intent.getAction(); + if (UserManager.ACTION_USER_RESTRICTIONS_CHANGED.equals(action)) { + boolean isUnmuteMicDisallowed = isUnmuteMicrophoneDisallowed(); + synchronized (mLock) { + if (isUnmuteMicDisallowed == mIsUnmuteMicDisallowed) { + return; + } + mIsUnmuteMicDisallowed = isUnmuteMicDisallowed; + } + if (mUserRestrictionsCallback != null) { + mUserRestrictionsCallback.onMicrophoneRestrictionChanged(isUnmuteMicDisallowed); + } + } + } +} diff --git a/core/java/android/companion/virtual/audio/VirtualAudioSession.java b/core/java/android/companion/virtual/audio/VirtualAudioSession.java index 4f4c33a8f7197..bc71bd65e9440 100644 --- a/core/java/android/companion/virtual/audio/VirtualAudioSession.java +++ b/core/java/android/companion/virtual/audio/VirtualAudioSession.java @@ -19,6 +19,7 @@ package android.companion.virtual.audio; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; +import android.companion.virtual.audio.UserRestrictionsDetector.UserRestrictionsCallback; import android.companion.virtual.audio.VirtualAudioDevice.AudioConfigurationChangeCallback; import android.content.Context; import android.media.AudioFormat; @@ -50,10 +51,11 @@ import java.util.concurrent.Executor; */ @VisibleForTesting public final class VirtualAudioSession extends IAudioSessionCallback.Stub implements - Closeable { + UserRestrictionsCallback, Closeable { private static final String TAG = "VirtualAudioSession"; private final Context mContext; + private final UserRestrictionsDetector mUserRestrictionsDetector; /** The {@link Executor} for sending {@link AudioConfigurationChangeCallback} to the caller */ private final Executor mExecutor; @Nullable @@ -81,6 +83,7 @@ public final class VirtualAudioSession extends IAudioSessionCallback.Stub implem public VirtualAudioSession(Context context, @Nullable AudioConfigurationChangeCallback callback, @Nullable Executor executor) { mContext = context; + mUserRestrictionsDetector = new UserRestrictionsDetector(context); mCallback = callback; mExecutor = executor != null ? executor : context.getMainExecutor(); } @@ -117,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( @@ -127,6 +130,7 @@ public final class VirtualAudioSession extends IAudioSessionCallback.Stub implem mInjectionFormat = injectionFormat; mAudioInjection = new AudioInjection(); mAudioInjection.play(); + mAudioInjection.setSilent(mUserRestrictionsDetector.isUnmuteMicrophoneDisallowed()); return mAudioInjection; } } @@ -170,6 +174,7 @@ public final class VirtualAudioSession extends IAudioSessionCallback.Stub implem @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) @Override public void close() { + mUserRestrictionsDetector.unregister(); releaseAudioStreams(); synchronized (mLock) { mAudioCapture = null; @@ -179,6 +184,15 @@ public final class VirtualAudioSession extends IAudioSessionCallback.Stub implem } } + @Override + public void onMicrophoneRestrictionChanged(boolean isUnmuteMicDisallowed) { + synchronized (mLock) { + if (mAudioInjection != null) { + mAudioInjection.setSilent(isUnmuteMicDisallowed); + } + } + } + @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) private void createAudioStreams(int[] appUids) { synchronized (mLock) {