Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b2ce7519 authored by Jean-Michel Trivi's avatar Jean-Michel Trivi
Browse files

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
parent 2ab34f64
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -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>
+1 −0
Original line number Diff line number Diff line
@@ -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" />
+50 −2
Original line number Diff line number Diff line
@@ -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(
+41 −1
Original line number Diff line number Diff line
@@ -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));
    }
}