diff options
7 files changed, 116 insertions, 34 deletions
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 71424b14373b..aa9cb1e99ac8 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -393,6 +393,7 @@ public class AudioService extends IAudioService.Stub private static final int MSG_DISPATCH_PREFERRED_MIXER_ATTRIBUTES = 52; private static final int MSG_LOWER_VOLUME_TO_RS1 = 53; private static final int MSG_CONFIGURATION_CHANGED = 54; + private static final int MSG_BROADCAST_MASTER_MUTE = 55; /** Messages handled by the {@link SoundDoseHelper}. */ /*package*/ static final int SAFE_MEDIA_VOLUME_MSG_START = 1000; @@ -975,6 +976,9 @@ public class AudioService extends IAudioService.Stub @GuardedBy("mSettingsLock") private boolean mRttEnabled = false; + private AtomicBoolean mMasterMute = new AtomicBoolean(false); + + /////////////////////////////////////////////////////////////////////////// // Construction /////////////////////////////////////////////////////////////////////////// @@ -2738,21 +2742,18 @@ public class AudioService extends IAudioService.Stub } final int currentUser = getCurrentUserId(); + if (mUseFixedVolume) { + AudioSystem.setMasterVolume(1.0f); + } + // Check the current user restriction. boolean masterMute = mUserManagerInternal.getUserRestriction(currentUser, UserManager.DISALLOW_UNMUTE_DEVICE) || mUserManagerInternal.getUserRestriction(currentUser, UserManager.DISALLOW_ADJUST_VOLUME); - if (mUseFixedVolume) { - masterMute = false; - AudioSystem.setMasterVolume(1.0f); - } - if (DEBUG_VOL) { - Log.d(TAG, String.format("Master mute %s, user=%d", masterMute, currentUser)); - } - AudioSystem.setMasterMute(masterMute); - broadcastMasterMuteStatus(masterMute); + setMasterMuteInternalNoCallerCheck( + masterMute, /* flags =*/ 0, currentUser, "readUserRestrictions"); mMicMuteFromRestrictions = mUserManagerInternal.getUserRestriction( currentUser, UserManager.DISALLOW_UNMUTE_MICROPHONE); @@ -4768,16 +4769,10 @@ public class AudioService extends IAudioService.Stub // UI update and Broadcast Intent private void sendMasterMuteUpdate(boolean muted, int flags) { mVolumeController.postMasterMuteChanged(updateFlagsForTvPlatform(flags)); - broadcastMasterMuteStatus(muted); + sendMsg(mAudioHandler, MSG_BROADCAST_MASTER_MUTE, + SENDMSG_QUEUE, muted ? 1 : 0, 0, null, 0); } - private void broadcastMasterMuteStatus(boolean muted) { - Intent intent = new Intent(AudioManager.MASTER_MUTE_CHANGED_ACTION); - intent.putExtra(AudioManager.EXTRA_MASTER_VOLUME_MUTED, muted); - intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT - | Intent.FLAG_RECEIVER_REPLACE_PENDING); - sendStickyBroadcastToAll(intent); - } /** * Sets the stream state's index, and posts a message to set system volume. @@ -4944,18 +4939,21 @@ public class AudioService extends IAudioService.Stub != PackageManager.PERMISSION_GRANTED) { return; } - setMasterMuteInternalNoCallerCheck(mute, flags, userId); + setMasterMuteInternalNoCallerCheck(mute, flags, userId, "setMasterMute"); } - private void setMasterMuteInternalNoCallerCheck(boolean mute, int flags, int userId) { + private void setMasterMuteInternalNoCallerCheck( + boolean mute, int flags, int userId, String eventSource) { if (DEBUG_VOL) { - Log.d(TAG, String.format("Master mute %s, %d, user=%d", mute, flags, userId)); + Log.d(TAG, TextUtils.formatSimple("Master mute %s, %d, user=%d from %s", + mute, flags, userId, eventSource)); } + if (!isPlatformAutomotive() && mUseFixedVolume) { // If using fixed volume, we don't mute. // TODO: remove the isPlatformAutomotive check here. // The isPlatformAutomotive check is added for safety but may not be necessary. - return; + mute = false; } // For automotive, // - the car service is always running as system user @@ -4964,8 +4962,10 @@ public class AudioService extends IAudioService.Stub // Therefore, the getCurrentUser() is always different to the foreground user. if ((isPlatformAutomotive() && userId == UserHandle.USER_SYSTEM) || (getCurrentUserId() == userId)) { - if (mute != AudioSystem.getMasterMute()) { - AudioSystem.setMasterMute(mute); + if (mute != mMasterMute.getAndSet(mute)) { + sVolumeLogger.enqueue(new VolumeEvent( + VolumeEvent.VOL_MASTER_MUTE, mute)); + mAudioSystem.setMasterMute(mute); sendMasterMuteUpdate(mute, flags); } } @@ -4973,7 +4973,7 @@ public class AudioService extends IAudioService.Stub /** get global mute state. */ public boolean isMasterMute() { - return AudioSystem.getMasterMute(); + return mMasterMute.get(); } @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) @@ -9278,6 +9278,10 @@ public class AudioService extends IAudioService.Stub mSystemServer.sendMicrophoneMuteChangedIntent(); break; + case MSG_BROADCAST_MASTER_MUTE: + mSystemServer.broadcastMasterMuteStatus(msg.arg1 == 1); + break; + case MSG_CHECK_MODE_FOR_UID: synchronized (mDeviceBroker.mSetModeLock) { if (msg.obj == null) { @@ -9675,7 +9679,8 @@ public class AudioService extends IAudioService.Stub newRestrictions.getBoolean(UserManager.DISALLOW_ADJUST_VOLUME) || newRestrictions.getBoolean(UserManager.DISALLOW_UNMUTE_DEVICE); if (wasRestricted != isRestricted) { - setMasterMuteInternalNoCallerCheck(isRestricted, /* flags =*/ 0, userId); + setMasterMuteInternalNoCallerCheck( + isRestricted, /* flags =*/ 0, userId, "onUserRestrictionsChanged"); } } } @@ -11033,10 +11038,11 @@ public class AudioService extends IAudioService.Stub pw.print(" mHdmiCecVolumeControlEnabled="); pw.println(mHdmiCecVolumeControlEnabled); } pw.print(" mIsCallScreeningModeSupported="); pw.println(mIsCallScreeningModeSupported); - pw.print(" mic mute FromSwitch=" + mMicMuteFromSwitch + pw.println(" mic mute FromSwitch=" + mMicMuteFromSwitch + " FromRestrictions=" + mMicMuteFromRestrictions + " FromApi=" + mMicMuteFromApi + " from system=" + mMicMuteFromSystemCached); + pw.print(" mMasterMute="); pw.println(mMasterMute.get()); dumpAccessibilityServiceUids(pw); dumpAssistantServicesUids(pw); diff --git a/services/core/java/com/android/server/audio/AudioServiceEvents.java b/services/core/java/com/android/server/audio/AudioServiceEvents.java index 6ebb42e08ade..aac868f45fe3 100644 --- a/services/core/java/com/android/server/audio/AudioServiceEvents.java +++ b/services/core/java/com/android/server/audio/AudioServiceEvents.java @@ -228,6 +228,7 @@ public class AudioServiceEvents { static final int VOL_MUTE_STREAM_INT = 9; static final int VOL_SET_LE_AUDIO_VOL = 10; static final int VOL_ADJUST_GROUP_VOL = 11; + static final int VOL_MASTER_MUTE = 12; final int mOp; final int mStream; @@ -321,6 +322,17 @@ public class AudioServiceEvents { logMetricEvent(); } + /** used for VOL_MASTER_MUTE */ + VolumeEvent(int op, boolean state) { + mOp = op; + mStream = -1; + mVal1 = state ? 1 : 0; + mVal2 = 0; + mCaller = null; + mGroupName = null; + logMetricEvent(); + } + /** * Audio Analytics unique Id. @@ -429,6 +441,9 @@ public class AudioServiceEvents { case VOL_MUTE_STREAM_INT: // No value in logging metrics for this internal event return; + case VOL_MASTER_MUTE: + // No value in logging metrics for this internal event + return; default: return; } @@ -510,6 +525,10 @@ public class AudioServiceEvents { .append(AudioSystem.streamToString(mStream)) .append(mVal1 == 1 ? ", muted)" : ", unmuted)") .toString(); + case VOL_MASTER_MUTE: + return new StringBuilder("Master mute:") + .append(mVal1 == 1 ? " muted)" : " unmuted)") + .toString(); default: return new StringBuilder("FIXME invalid op:").append(mOp).toString(); } } diff --git a/services/core/java/com/android/server/audio/AudioSystemAdapter.java b/services/core/java/com/android/server/audio/AudioSystemAdapter.java index 43438942a060..e70b6497538e 100644 --- a/services/core/java/com/android/server/audio/AudioSystemAdapter.java +++ b/services/core/java/com/android/server/audio/AudioSystemAdapter.java @@ -685,6 +685,15 @@ public class AudioSystemAdapter implements AudioSystem.RoutingUpdateCallback, } /** + * Sets master mute state in audio flinger + * @param mute the mute state to set + * @return operation status + */ + public int setMasterMute(boolean mute) { + return AudioSystem.setMasterMute(mute); + } + + /** * Part of AudioService dump * @param pw */ diff --git a/services/core/java/com/android/server/audio/SystemServerAdapter.java b/services/core/java/com/android/server/audio/SystemServerAdapter.java index 22456bcf3f66..dfcd2e9a8a39 100644 --- a/services/core/java/com/android/server/audio/SystemServerAdapter.java +++ b/services/core/java/com/android/server/audio/SystemServerAdapter.java @@ -145,4 +145,18 @@ public class SystemServerAdapter { ActivityManager.broadcastStickyIntent(intent, profileId); } } + + /*package*/ void broadcastMasterMuteStatus(boolean muted) { + Intent intent = new Intent(AudioManager.MASTER_MUTE_CHANGED_ACTION); + intent.putExtra(AudioManager.EXTRA_MASTER_VOLUME_MUTED, muted); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT + | Intent.FLAG_RECEIVER_REPLACE_PENDING + | Intent.FLAG_RECEIVER_FOREGROUND); + final long ident = Binder.clearCallingIdentity(); + try { + mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); + } finally { + Binder.restoreCallingIdentity(ident); + } + } } diff --git a/services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java b/services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java index 88d57ac1ab88..e565faa1c00b 100644 --- a/services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java @@ -18,7 +18,7 @@ package com.android.server.audio; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.after; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; @@ -42,10 +42,10 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.junit.MockitoJUnit; -import org.mockito.junit.MockitoRule; import org.mockito.Mock; import org.mockito.Spy; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; @MediumTest @RunWith(AndroidJUnit4.class) @@ -58,7 +58,7 @@ public class AudioServiceTest { public final MockitoRule mockito = MockitoJUnit.rule(); private Context mContext; - private AudioSystemAdapter mAudioSystem; + private AudioSystemAdapter mSpyAudioSystem; private SettingsAdapter mSettingsAdapter; @Spy private NoOpSystemServerAdapter mSpySystemServer; @@ -78,11 +78,11 @@ public class AudioServiceTest { sLooperPrepared = true; } mContext = InstrumentationRegistry.getTargetContext(); - mAudioSystem = new NoOpAudioSystemAdapter(); + mSpyAudioSystem = spy(new NoOpAudioSystemAdapter()); mSettingsAdapter = new NoOpSettingsAdapter(); when(mMockAppOpsManager.noteOp(anyInt(), anyInt(), anyString(), anyString(), anyString())) .thenReturn(AppOpsManager.MODE_ALLOWED); - mAudioService = new AudioService(mContext, mAudioSystem, mSpySystemServer, + mAudioService = new AudioService(mContext, mSpyAudioSystem, mSpySystemServer, mSettingsAdapter, mMockAudioPolicy, null, mMockAppOpsManager, mMockPermissionEnforcer); } @@ -95,7 +95,7 @@ public class AudioServiceTest { public void testMuteMicrophone() throws Exception { Log.i(TAG, "running testMuteMicrophone"); Assert.assertNotNull(mAudioService); - final NoOpAudioSystemAdapter testAudioSystem = (NoOpAudioSystemAdapter) mAudioSystem; + final NoOpAudioSystemAdapter testAudioSystem = (NoOpAudioSystemAdapter) mSpyAudioSystem; testAudioSystem.configureMuteMicrophoneToFail(false); for (boolean muted : new boolean[] { true, false}) { testAudioSystem.configureIsMicrophoneMuted(!muted); @@ -120,7 +120,7 @@ public class AudioServiceTest { public void testMuteMicrophoneWhenFail() throws Exception { Log.i(TAG, "running testMuteMicrophoneWhenFail"); Assert.assertNotNull(mAudioService); - final NoOpAudioSystemAdapter testAudioSystem = (NoOpAudioSystemAdapter) mAudioSystem; + final NoOpAudioSystemAdapter testAudioSystem = (NoOpAudioSystemAdapter) mSpyAudioSystem; testAudioSystem.configureMuteMicrophoneToFail(true); for (boolean muted : new boolean[] { true, false}) { testAudioSystem.configureIsMicrophoneMuted(!muted); @@ -175,4 +175,28 @@ public class AudioServiceTest { Assert.assertEquals(false, mAudioService.isHotwordStreamSupported(false)); Assert.assertEquals(false, mAudioService.isHotwordStreamSupported(true)); } + + /** + * Test master mute setter and getter + */ + @Test + public void testMasterMute() throws Exception { + Log.i(TAG, "running testMasterMute"); + Assert.assertNotNull(mAudioService); + for (boolean mute : new boolean[] { true, false}) { + boolean wasMute = mAudioService.isMasterMute(); + mAudioService.setMasterMute(mute, 0 /* flags */, mContext.getOpPackageName(), + UserHandle.getCallingUserId(), null); + + Assert.assertEquals("master mute reporting wrong value", + mute, mAudioService.isMasterMute()); + + verify(mSpyAudioSystem, times(wasMute == mute ? 0 : 1)).setMasterMute(mute); + // verify the intent for master mute changed is supposed to be fired + verify(mSpySystemServer, + after(MAX_MESSAGE_HANDLING_DELAY_MS).times(wasMute == mute ? 0 : 1)) + .broadcastMasterMuteStatus(mute); + reset(mSpySystemServer); + } + } } diff --git a/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java b/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java index 08a0878e6e7c..0eac718c2f14 100644 --- a/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java +++ b/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java @@ -142,4 +142,9 @@ public class NoOpAudioSystemAdapter extends AudioSystemAdapter { @NonNull AudioAttributes attributes, boolean forVolume) { return new ArrayList<>(); } + + @Override + public int setMasterMute(boolean muted) { + return AudioSystem.AUDIO_STATUS_OK; + } } diff --git a/services/tests/servicestests/src/com/android/server/audio/NoOpSystemServerAdapter.java b/services/tests/servicestests/src/com/android/server/audio/NoOpSystemServerAdapter.java index 83c566376e44..a715f519e667 100644 --- a/services/tests/servicestests/src/com/android/server/audio/NoOpSystemServerAdapter.java +++ b/services/tests/servicestests/src/com/android/server/audio/NoOpSystemServerAdapter.java @@ -39,4 +39,9 @@ public class NoOpSystemServerAdapter extends SystemServerAdapter { public void sendDeviceBecomingNoisyIntent() { // no-op } + + @Override + public void broadcastMasterMuteStatus(boolean muted) { + // no-op + } } |