AudioService: config for splitting notif and ring volumes

Add support for ability to configure device with a choice
of aliasing notification and ringtone volumes (default)
or not (ringtone volume is separately controlled from
notification volume):
- add config_alias_ring_notif_stream_types property
 for per-device configuration
- update voice stream aliases based on property
- in audio dump: output aliases when present
For testing: add ability to inject AppOpsManager
 in the AudioService constructor so a mock can
 be injected.

Bug: 228180614
Test: atest AudioServiceTest#testRingNotifAlias
Change-Id: I8b0ea86aecd398b76dbaa920e19727d0dd3d6eb0
Merged-In: I8b0ea86aecd398b76dbaa920e19727d0dd3d6eb0
This commit is contained in:
Jean-Michel Trivi
2022-06-06 16:40:14 +00:00
parent 2ab34f6494
commit b2ce7519b3
4 changed files with 96 additions and 3 deletions

View File

@@ -2030,6 +2030,10 @@
STREAM_MUSIC as if it's on TV platform. -->
<bool name="config_single_volume">false</bool>
<!-- Flag indicating whether notification and ringtone volumes
are controlled together (aliasing is true) or not. -->
<bool name="config_alias_ring_notif_stream_types">true</bool>
<!-- Flag indicating whether platform level volume adjustments are enabled for remote sessions
on grouped devices. -->
<bool name="config_volumeAdjustmentForRemoteGroupSessions">true</bool>

View File

@@ -273,6 +273,7 @@
<java-symbol type="attr" name="autofillSaveCustomSubtitleMaxHeight"/>
<java-symbol type="bool" name="action_bar_embed_tabs" />
<java-symbol type="bool" name="action_bar_expanded_action_views_exclusive" />
<java-symbol type="bool" name="config_alias_ring_notif_stream_types" />
<java-symbol type="bool" name="config_avoidGfxAccel" />
<java-symbol type="bool" name="config_bluetooth_address_validation" />
<java-symbol type="integer" name="config_chooser_max_targets_per_row" />

View File

@@ -275,6 +275,25 @@ public class AudioService extends IAudioService.Stub
// indicates whether the system maps all streams to a single stream.
private final boolean mIsSingleVolume;
/**
* indicates whether STREAM_NOTIFICATION is aliased to STREAM_RING
* not final due to test method, see {@link #setNotifAliasRingForTest(boolean)}.
*/
private boolean mNotifAliasRing;
/**
* Test method to temporarily override whether STREAM_NOTIFICATION is aliased to STREAM_RING,
* volumes will be updated in case of a change.
* @param alias if true, STREAM_NOTIFICATION is aliased to STREAM_RING
*/
/*package*/ void setNotifAliasRingForTest(boolean alias) {
boolean update = (mNotifAliasRing != alias);
mNotifAliasRing = alias;
if (update) {
updateStreamVolumeAlias(true, "AudioServiceTest");
}
}
/*package*/ boolean isPlatformVoice() {
return mPlatformType == AudioSystem.PLATFORM_VOICE;
}
@@ -942,10 +961,25 @@ public class AudioService extends IAudioService.Stub
*/
public AudioService(Context context, AudioSystemAdapter audioSystem,
SystemServerAdapter systemServer, SettingsAdapter settings, @Nullable Looper looper) {
this (context, audioSystem, systemServer, settings, looper,
context.getSystemService(AppOpsManager.class));
}
/**
* @param context
* @param audioSystem Adapter for {@link AudioSystem}
* @param systemServer Adapter for privilieged functionality for system server components
* @param settings Adapter for {@link Settings}
* @param looper Looper to use for the service's message handler. If this is null, an
* {@link AudioSystemThread} is created as the messaging thread instead.
*/
public AudioService(Context context, AudioSystemAdapter audioSystem,
SystemServerAdapter systemServer, SettingsAdapter settings, @Nullable Looper looper,
AppOpsManager appOps) {
sLifecycleLogger.log(new AudioEventLogger.StringEvent("AudioService()"));
mContext = context;
mContentResolver = context.getContentResolver();
mAppOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE);
mAppOps = appOps;
mAudioSystem = audioSystem;
mSystemServer = systemServer;
@@ -976,6 +1010,9 @@ public class AudioService extends IAudioService.Stub
mUseVolumeGroupAliases = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_handleVolumeAliasesUsingVolumeGroups);
mNotifAliasRing = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_alias_ring_notif_stream_types);
// Initialize volume
// Priority 1 - Android Property
// Priority 2 - Audio Policy Service
@@ -2008,7 +2045,13 @@ public class AudioService extends IAudioService.Stub
pw.println("\nStream volumes (device: index)");
int numStreamTypes = AudioSystem.getNumStreamTypes();
for (int i = 0; i < numStreamTypes; i++) {
pw.println("- " + AudioSystem.STREAM_NAMES[i] + ":");
StringBuilder alias = new StringBuilder();
if (mStreamVolumeAlias[i] != i) {
alias.append(" (aliased to: ")
.append(AudioSystem.STREAM_NAMES[mStreamVolumeAlias[i]])
.append(")");
}
pw.println("- " + AudioSystem.STREAM_NAMES[i] + alias + ":");
mStreamStates[i].dump(pw);
pw.println("");
}
@@ -2040,6 +2083,10 @@ public class AudioService extends IAudioService.Stub
mStreamVolumeAlias = STREAM_VOLUME_ALIAS_DEFAULT;
dtmfStreamAlias = AudioSystem.STREAM_MUSIC;
}
if (!mNotifAliasRing) {
mStreamVolumeAlias[AudioSystem.STREAM_NOTIFICATION] =
AudioSystem.STREAM_NOTIFICATION;
}
}
if (mIsSingleVolume) {
@@ -9888,6 +9935,7 @@ public class AudioService extends IAudioService.Stub
pw.print(" mBtScoOnByApp="); pw.println(mBtScoOnByApp);
pw.print(" mIsSingleVolume="); pw.println(mIsSingleVolume);
pw.print(" mUseFixedVolume="); pw.println(mUseFixedVolume);
pw.print(" mNotifAliasRing="); pw.println(mNotifAliasRing);
pw.print(" mFixedVolumeDevices="); pw.println(dumpDeviceTypes(mFixedVolumeDevices));
pw.print(" mFullVolumeDevices="); pw.println(dumpDeviceTypes(mFullVolumeDevices));
pw.print(" mAbsoluteVolumeDevices.keySet()="); pw.println(dumpDeviceTypes(

View File

@@ -15,12 +15,18 @@
*/
package com.android.server.audio;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.AppOpsManager;
import android.content.Context;
import android.media.AudioSystem;
import android.os.Looper;
import android.os.UserHandle;
import android.util.Log;
@@ -33,6 +39,7 @@ import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Spy;
@MediumTest
@@ -46,6 +53,7 @@ public class AudioServiceTest {
private AudioSystemAdapter mAudioSystem;
@Spy private SystemServerAdapter mSpySystemServer;
private SettingsAdapter mSettingsAdapter;
@Mock private AppOpsManager mMockAppOpsManager;
// the class being unit-tested here
private AudioService mAudioService;
@@ -61,8 +69,11 @@ public class AudioServiceTest {
mAudioSystem = new NoOpAudioSystemAdapter();
mSpySystemServer = spy(new NoOpSystemServerAdapter());
mSettingsAdapter = new NoOpSettingsAdapter();
mMockAppOpsManager = mock(AppOpsManager.class);
when(mMockAppOpsManager.noteOp(anyInt(), anyInt(), anyString(), anyString(), anyString()))
.thenReturn(AppOpsManager.MODE_ALLOWED);
mAudioService = new AudioService(mContext, mAudioSystem, mSpySystemServer,
mSettingsAdapter, null);
mSettingsAdapter, null, mMockAppOpsManager);
}
/**
@@ -113,4 +124,33 @@ public class AudioServiceTest {
reset(mSpySystemServer);
}
}
@Test
public void testRingNotifAlias() throws Exception {
Log.i(TAG, "running testRingNotifAlias");
Assert.assertNotNull(mAudioService);
// TODO add initialization message that can be caught here instead of sleeping
Thread.sleep(MAX_MESSAGE_HANDLING_DELAY_MS); // wait for full AudioService initialization
// test with aliasing RING and NOTIFICATION
mAudioService.setNotifAliasRingForTest(true);
final int ringMaxVol = mAudioService.getStreamMaxVolume(AudioSystem.STREAM_RING);
final int ringMinVol = mAudioService.getStreamMinVolume(AudioSystem.STREAM_RING);
final int ringVol = ringMinVol + 1;
// set a value for NOTIFICATION so it's not at the target test value (ringMaxVol)
mAudioService.setStreamVolume(AudioSystem.STREAM_NOTIFICATION,
ringVol, 0, "bla");
mAudioService.setStreamVolume(AudioSystem.STREAM_RING, ringMaxVol, 0, "bla");
Thread.sleep(MAX_MESSAGE_HANDLING_DELAY_MS);
Assert.assertEquals(ringMaxVol,
mAudioService.getStreamVolume(AudioSystem.STREAM_NOTIFICATION));
// test with no aliasing between RING and NOTIFICATION
mAudioService.setNotifAliasRingForTest(false);
mAudioService.setStreamVolume(AudioSystem.STREAM_RING, ringVol, 0, "bla");
mAudioService.setStreamVolume(AudioSystem.STREAM_NOTIFICATION, ringMaxVol, 0, "bla");
Assert.assertEquals(ringVol, mAudioService.getStreamVolume(AudioSystem.STREAM_RING));
Assert.assertEquals(ringMaxVol, mAudioService.getStreamVolume(
AudioSystem.STREAM_NOTIFICATION));
}
}