chromium/chrome/android/javatests/src/org/chromium/chrome/browser/media/ui/AutoplayMutedNotificationTest.java

// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.chrome.browser.media.ui;

import static org.chromium.base.test.util.Restriction.RESTRICTION_TYPE_NON_LOW_END_DEVICE;

import android.content.Context;
import android.media.AudioManager;

import androidx.test.core.app.ApplicationProvider;
import androidx.test.filters.SmallTest;

import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.base.test.util.Restriction;
import org.chromium.chrome.browser.flags.ChromeSwitches;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
import org.chromium.chrome.test.R;
import org.chromium.components.browser_ui.media.MediaNotificationManager;
import org.chromium.content_public.browser.test.util.DOMUtils;
import org.chromium.content_public.browser.test.util.JavaScriptUtils;
import org.chromium.net.test.EmbeddedTestServer;

/**
 * Integration test that checks that autoplay muted doesn't show a notification nor take audio focus
 */
@RunWith(ChromeJUnit4ClassRunner.class)
@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
public class AutoplayMutedNotificationTest {
    @Rule
    public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule();

    private static final String TEST_PATH = "/content/test/data/media/session/autoplay-muted.html";
    private static final String VIDEO_ID = "video";
    private static final String PLAY_BUTTON_ID = "play";
    private static final String UNMUTE_BUTTON_ID = "unmute";
    private static final int AUDIO_FOCUS_CHANGE_TIMEOUT = 500; // ms

    private EmbeddedTestServer mTestServer;

    private AudioManager getAudioManager() {
        return (AudioManager)
                mActivityTestRule
                        .getActivity()
                        .getApplicationContext()
                        .getSystemService(Context.AUDIO_SERVICE);
    }

    private boolean isMediaNotificationVisible() {
        return MediaNotificationManager.getController(R.id.media_playback_notification) != null;
    }

    private class MockAudioFocusChangeListener implements AudioManager.OnAudioFocusChangeListener {
        private int mAudioFocusState = AudioManager.AUDIOFOCUS_LOSS;

        @Override
        public void onAudioFocusChange(int focusChange) {
            mAudioFocusState = focusChange;
        }

        public int getAudioFocusState() {
            return mAudioFocusState;
        }

        public void requestAudioFocus(int focusType) {
            int result =
                    getAudioManager().requestAudioFocus(this, AudioManager.STREAM_MUSIC, focusType);
            if (result != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
                Assert.fail("Did not get audio focus");
            } else {
                mAudioFocusState = focusType;
            }
        }
    }

    private MockAudioFocusChangeListener mAudioFocusChangeListener;

    @Before
    public void setUp() {
        mTestServer =
                EmbeddedTestServer.createAndStartServer(
                        ApplicationProvider.getApplicationContext());
        mAudioFocusChangeListener = new MockAudioFocusChangeListener();
        mActivityTestRule.startMainActivityWithURL(mTestServer.getURL(TEST_PATH));
    }

    @Test
    @SmallTest
    @Restriction(RESTRICTION_TYPE_NON_LOW_END_DEVICE)
    public void testBasic() throws Exception {
        Tab tab = mActivityTestRule.getActivity().getActivityTab();

        // Taking audio focus.
        Assert.assertEquals(
                AudioManager.AUDIOFOCUS_LOSS, mAudioFocusChangeListener.getAudioFocusState());
        mAudioFocusChangeListener.requestAudioFocus(AudioManager.AUDIOFOCUS_GAIN);
        Assert.assertEquals(
                AudioManager.AUDIOFOCUS_GAIN, mAudioFocusChangeListener.getAudioFocusState());

        // The page will autoplay the video.
        DOMUtils.waitForMediaPlay(tab.getWebContents(), VIDEO_ID);

        // Audio focus notification is OS-driven.
        Thread.sleep(AUDIO_FOCUS_CHANGE_TIMEOUT);

        // Audio focus was not taken and no notification is visible.
        Assert.assertEquals(
                AudioManager.AUDIOFOCUS_GAIN, mAudioFocusChangeListener.getAudioFocusState());
        Assert.assertFalse(isMediaNotificationVisible());
    }

    @Test
    @SmallTest
    @Restriction(RESTRICTION_TYPE_NON_LOW_END_DEVICE)
    public void testDoesNotReactToAudioFocus() throws Exception {
        Tab tab = mActivityTestRule.getActivity().getActivityTab();

        // The page will autoplay the video.
        DOMUtils.waitForMediaPlay(tab.getWebContents(), VIDEO_ID);

        // Taking audio focus.
        Assert.assertEquals(
                AudioManager.AUDIOFOCUS_LOSS, mAudioFocusChangeListener.getAudioFocusState());
        mAudioFocusChangeListener.requestAudioFocus(AudioManager.AUDIOFOCUS_GAIN);
        Assert.assertEquals(
                AudioManager.AUDIOFOCUS_GAIN, mAudioFocusChangeListener.getAudioFocusState());

        // Audio focus notification is OS-driven.
        Thread.sleep(AUDIO_FOCUS_CHANGE_TIMEOUT);

        // Video did not pause.
        Assert.assertFalse(DOMUtils.isMediaPaused(tab.getWebContents(), VIDEO_ID));

        // Still no notification.
        Assert.assertFalse(isMediaNotificationVisible());
    }

    @Test
    @SmallTest
    @Restriction(RESTRICTION_TYPE_NON_LOW_END_DEVICE)
    public void testAutoplayMutedThenUnmute() throws Exception {
        Tab tab = mActivityTestRule.getActivity().getActivityTab();

        // Taking audio focus.
        Assert.assertEquals(
                AudioManager.AUDIOFOCUS_LOSS, mAudioFocusChangeListener.getAudioFocusState());
        mAudioFocusChangeListener.requestAudioFocus(AudioManager.AUDIOFOCUS_GAIN);
        Assert.assertEquals(
                AudioManager.AUDIOFOCUS_GAIN, mAudioFocusChangeListener.getAudioFocusState());

        // The page will autoplay the video.
        DOMUtils.waitForMediaPlay(tab.getWebContents(), VIDEO_ID);

        StringBuilder sb = new StringBuilder();
        sb.append("(function() {");
        sb.append("  var video = document.querySelector('video');");
        sb.append("  video.muted = false;");
        sb.append("  return video.muted;");
        sb.append("})();");

        // Unmute from script.
        String result =
                JavaScriptUtils.executeJavaScriptAndWaitForResult(
                        tab.getWebContents(), sb.toString());
        Assert.assertTrue(result.trim().equalsIgnoreCase("false"));

        // Video is paused.
        Assert.assertTrue(DOMUtils.isMediaPaused(tab.getWebContents(), VIDEO_ID));

        // Audio focus notification is OS-driven.
        Thread.sleep(AUDIO_FOCUS_CHANGE_TIMEOUT);

        // Audio focus was not taken and no notification is visible.
        Assert.assertEquals(
                AudioManager.AUDIOFOCUS_GAIN, mAudioFocusChangeListener.getAudioFocusState());
        Assert.assertFalse(isMediaNotificationVisible());
    }

    @Test
    @SmallTest
    @Restriction(RESTRICTION_TYPE_NON_LOW_END_DEVICE)
    public void testMutedPlaybackDoesNotTakeAudioFocus() throws Exception {
        Tab tab = mActivityTestRule.getActivity().getActivityTab();

        // Taking audio focus.
        Assert.assertEquals(
                AudioManager.AUDIOFOCUS_LOSS, mAudioFocusChangeListener.getAudioFocusState());
        mAudioFocusChangeListener.requestAudioFocus(AudioManager.AUDIOFOCUS_GAIN);
        Assert.assertEquals(
                AudioManager.AUDIOFOCUS_GAIN, mAudioFocusChangeListener.getAudioFocusState());

        // The page will autoplay the video.
        DOMUtils.waitForMediaPlay(tab.getWebContents(), VIDEO_ID);

        // Audio focus notification is OS-driven.
        Thread.sleep(AUDIO_FOCUS_CHANGE_TIMEOUT);

        DOMUtils.pauseMedia(tab.getWebContents(), VIDEO_ID);

        // Restart the video with a gesture: no longer "muted autoplay".
        DOMUtils.clickNode(tab.getWebContents(), PLAY_BUTTON_ID);
        DOMUtils.waitForMediaPlay(tab.getWebContents(), VIDEO_ID);

        // Audio focus was not taken and no notification is visible.
        Assert.assertEquals(
                AudioManager.AUDIOFOCUS_GAIN, mAudioFocusChangeListener.getAudioFocusState());
        Assert.assertFalse(isMediaNotificationVisible());
    }

    @Test
    @SmallTest
    @Restriction(RESTRICTION_TYPE_NON_LOW_END_DEVICE)
    public void testUnmutedPlaybackTakesAudioFocus() throws Exception {
        Tab tab = mActivityTestRule.getActivity().getActivityTab();

        // Taking audio focus.
        Assert.assertEquals(
                AudioManager.AUDIOFOCUS_LOSS, mAudioFocusChangeListener.getAudioFocusState());
        mAudioFocusChangeListener.requestAudioFocus(AudioManager.AUDIOFOCUS_GAIN);
        Assert.assertEquals(
                AudioManager.AUDIOFOCUS_GAIN, mAudioFocusChangeListener.getAudioFocusState());

        // The page will autoplay the video.
        DOMUtils.waitForMediaPlay(tab.getWebContents(), VIDEO_ID);

        // Audio focus notification is OS-driven.
        Thread.sleep(AUDIO_FOCUS_CHANGE_TIMEOUT);

        // Restart the video with a gesture: no longer "muted autoplay".
        DOMUtils.clickNode(tab.getWebContents(), UNMUTE_BUTTON_ID);
        Assert.assertFalse(DOMUtils.isMediaPaused(tab.getWebContents(), VIDEO_ID));

        Thread.sleep(AUDIO_FOCUS_CHANGE_TIMEOUT);

        // Audio focus was taken and a notification is visible.
        Assert.assertNotEquals(
                AudioManager.AUDIOFOCUS_GAIN, mAudioFocusChangeListener.getAudioFocusState());
        Assert.assertTrue(isMediaNotificationVisible());
    }
}