blob: 335bbc5b4f839735daf99fd8dc2f8a8b865ce6dc [file] [log] [blame]
/*
* Copyright (C) 2021 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.media.metrics.cts;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import android.cts.statsdatom.lib.AtomTestUtils;
import android.cts.statsdatom.lib.ConfigUtils;
import android.cts.statsdatom.lib.DeviceUtils;
import android.cts.statsdatom.lib.ReportUtils;
import com.android.ddmlib.testrunner.RemoteAndroidTestRunner;
import com.android.os.AtomsProto;
import com.android.os.StatsLog;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.invoker.TestInformation;
import com.android.tradefed.log.LogUtil;
import com.android.tradefed.metrics.proto.MetricMeasurement;
import com.android.tradefed.result.CollectingTestListener;
import com.android.tradefed.result.TestDescription;
import com.android.tradefed.result.TestResult;
import com.android.tradefed.result.TestRunResult;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import com.android.tradefed.testtype.junit4.AfterClassWithInfo;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
import com.android.tradefed.testtype.junit4.BeforeClassWithInfo;
import com.google.common.truth.Correspondence;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.io.FileNotFoundException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@RunWith(DeviceJUnit4ClassRunner.class)
public class MediaMetricsAtomTests extends BaseHostJUnit4Test {
private static final String TEST_RUNNER = "androidx.test.runner.AndroidJUnitRunner";
private static final String TAG = "MediaMetricsAtomTests";
public static final String TEST_APK = "CtsMediaMetricsHostTestApp.apk";
public static final String TEST_PKG = "android.media.metrics.cts";
private static final String FEATURE_AUDIO_OUTPUT = "android.hardware.audio.output";
private static final String FEATURE_MICROPHONE = "android.hardware.microphone";
private static final int MAX_BUFFER_CAPACITY = 30 * 1024 * 1024; // 30M
@BeforeClassWithInfo
public static void installApp(TestInformation testInfo)
throws DeviceNotAvailableException, FileNotFoundException {
assertThat(testInfo.getBuildInfo()).isNotNull();
DeviceUtils.installTestApp(testInfo.getDevice(), TEST_APK, TEST_PKG,
testInfo.getBuildInfo());
}
@Before
public void setUp() throws Exception {
ConfigUtils.removeConfig(getDevice());
ReportUtils.clearReports(getDevice());
Thread.sleep(AtomTestUtils.WAIT_TIME_LONG);
}
@After
public void tearDown() throws Exception {
ConfigUtils.removeConfig(getDevice());
ReportUtils.clearReports(getDevice());
}
@AfterClassWithInfo
public static void uninstallApp(TestInformation testInfo) throws Exception {
DeviceUtils.uninstallTestApp(testInfo.getDevice(), TEST_PKG);
}
@Test
public void testPlaybackStateEvent_default() throws Exception {
ConfigUtils.uploadConfigForPushedAtom(getDevice(), TEST_PKG,
AtomsProto.Atom.MEDIA_PLAYBACK_STATE_CHANGED_FIELD_NUMBER);
runDeviceTests(getDevice(), TEST_PKG,
"android.media.metrics.cts.MediaMetricsAtomHostSideTests",
"testPlaybackStateEvent_default", new LogSessionIdListener());
Thread.sleep(AtomTestUtils.WAIT_TIME_LONG);
List<StatsLog.EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
assertThat(data.size()).isEqualTo(1);
assertThat(data.get(0).getAtom().hasMediaPlaybackStateChanged()).isTrue();
AtomsProto.MediaPlaybackStateChanged result = data.get(
0).getAtom().getMediaPlaybackStateChanged();
assertThat(result.getPlaybackState().toString()).isEqualTo("NOT_STARTED");
assertThat(result.getTimeSincePlaybackCreatedMillis()).isEqualTo(-1L);
}
@Test
public void testPlaybackStateEvent() throws Exception {
ConfigUtils.uploadConfigForPushedAtom(getDevice(), TEST_PKG,
AtomsProto.Atom.MEDIA_PLAYBACK_STATE_CHANGED_FIELD_NUMBER);
runDeviceTests(getDevice(), TEST_PKG,
"android.media.metrics.cts.MediaMetricsAtomHostSideTests",
"testPlaybackStateEvent", new LogSessionIdListener());
Thread.sleep(AtomTestUtils.WAIT_TIME_LONG);
List<StatsLog.EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
assertThat(data.size()).isEqualTo(1);
assertThat(data.get(0).getAtom().hasMediaPlaybackStateChanged()).isTrue();
AtomsProto.MediaPlaybackStateChanged result = data.get(
0).getAtom().getMediaPlaybackStateChanged();
assertThat(result.getPlaybackState().toString()).isEqualTo("JOINING_FOREGROUND");
assertThat(result.getTimeSincePlaybackCreatedMillis()).isEqualTo(1763L);
}
// same as testPlaybackStateEvent, but we use the BundleSession transport.
@Test
public void testBundleSessionPlaybackStateEvent() throws Exception {
ConfigUtils.uploadConfigForPushedAtom(getDevice(), TEST_PKG,
AtomsProto.Atom.MEDIA_PLAYBACK_STATE_CHANGED_FIELD_NUMBER);
runDeviceTests(getDevice(), TEST_PKG,
"android.media.metrics.cts.MediaMetricsAtomHostSideTests",
"testBundleSessionPlaybackStateEvent", new LogSessionIdListener());
Thread.sleep(AtomTestUtils.WAIT_TIME_LONG);
List<StatsLog.EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
assertThat(data.size()).isEqualTo(1);
assertThat(data.get(0).getAtom().hasMediaPlaybackStateChanged()).isTrue();
AtomsProto.MediaPlaybackStateChanged result = data.get(
0).getAtom().getMediaPlaybackStateChanged();
assertThat(result.getPlaybackState().toString()).isEqualTo("JOINING_FOREGROUND");
assertThat(result.getTimeSincePlaybackCreatedMillis()).isEqualTo(1763L);
}
@Test
public void testPlaybackErrorEvent_default() throws Exception {
ConfigUtils.uploadConfigForPushedAtom(getDevice(), TEST_PKG,
AtomsProto.Atom.MEDIA_PLAYBACK_ERROR_REPORTED_FIELD_NUMBER);
runDeviceTests(getDevice(), TEST_PKG,
"android.media.metrics.cts.MediaMetricsAtomHostSideTests",
"testPlaybackErrorEvent_default", new LogSessionIdListener());
Thread.sleep(AtomTestUtils.WAIT_TIME_LONG);
List<StatsLog.EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
assertThat(data.size()).isEqualTo(1);
assertThat(data.get(0).getAtom().hasMediaPlaybackErrorReported()).isTrue();
AtomsProto.MediaPlaybackErrorReported result = data.get(
0).getAtom().getMediaPlaybackErrorReported();
assertThat(result.getTimeSincePlaybackCreatedMillis()).isEqualTo(-1L);
assertThat(result.getErrorCode().toString()).isEqualTo("ERROR_CODE_UNKNOWN");
assertThat(result.getSubErrorCode()).isEqualTo(0);
assertThat(result.getExceptionStack().startsWith(
"android.media.metrics.cts.MediaMetricsAtomHostSideTests"
+ ".testPlaybackErrorEvent")).isTrue();
}
@Test
public void testPlaybackErrorEvent() throws Exception {
ConfigUtils.uploadConfigForPushedAtom(getDevice(), TEST_PKG,
AtomsProto.Atom.MEDIA_PLAYBACK_ERROR_REPORTED_FIELD_NUMBER);
runDeviceTests(getDevice(), TEST_PKG,
"android.media.metrics.cts.MediaMetricsAtomHostSideTests",
"testPlaybackErrorEvent", new LogSessionIdListener());
Thread.sleep(AtomTestUtils.WAIT_TIME_LONG);
List<StatsLog.EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
assertThat(data.size()).isEqualTo(1);
assertThat(data.get(0).getAtom().hasMediaPlaybackErrorReported()).isTrue();
AtomsProto.MediaPlaybackErrorReported result = data.get(
0).getAtom().getMediaPlaybackErrorReported();
assertThat(result.getTimeSincePlaybackCreatedMillis()).isEqualTo(17630000L);
assertThat(result.getErrorCode().toString()).isEqualTo("ERROR_CODE_RUNTIME");
assertThat(result.getSubErrorCode()).isEqualTo(378);
assertThat(result.getExceptionStack().startsWith(
"android.media.metrics.cts.MediaMetricsAtomHostSideTests"
+ ".testPlaybackErrorEvent")).isTrue();
}
@Test
public void testTrackChangeEvent_default() throws Exception {
ConfigUtils.uploadConfigForPushedAtom(getDevice(), TEST_PKG,
AtomsProto.Atom.MEDIA_PLAYBACK_TRACK_CHANGED_FIELD_NUMBER);
runDeviceTests(getDevice(), TEST_PKG,
"android.media.metrics.cts.MediaMetricsAtomHostSideTests",
"testTrackChangeEvent_default", new LogSessionIdListener());
Thread.sleep(AtomTestUtils.WAIT_TIME_LONG);
List<StatsLog.EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
assertThat(data.size()).isEqualTo(1);
assertThat(data.get(0).getAtom().hasMediaPlaybackTrackChanged()).isTrue();
AtomsProto.MediaPlaybackTrackChanged result = data.get(
0).getAtom().getMediaPlaybackTrackChanged();
assertThat(result.getTimeSincePlaybackCreatedMillis()).isEqualTo(-1L);
assertThat(result.getState().toString()).isEqualTo("OFF");
assertThat(result.getReason().toString()).isEqualTo("REASON_UNKNOWN");
assertThat(result.getContainerMimeType()).isEqualTo("");
assertThat(result.getSampleMimeType()).isEqualTo("");
assertThat(result.getCodecName()).isEqualTo("");
assertThat(result.getBitrate()).isEqualTo(-1);
assertThat(result.getType().toString()).isEqualTo("AUDIO");
assertThat(result.getLanguage()).isEqualTo("");
assertThat(result.getLanguageRegion()).isEqualTo("");
assertThat(result.getSampleRate()).isEqualTo(-1);
assertThat(result.getChannelCount()).isEqualTo(-1);
}
@Test
public void testTrackChangeEvent_text() throws Exception {
ConfigUtils.uploadConfigForPushedAtom(getDevice(), TEST_PKG,
AtomsProto.Atom.MEDIA_PLAYBACK_TRACK_CHANGED_FIELD_NUMBER);
runDeviceTests(getDevice(), TEST_PKG,
"android.media.metrics.cts.MediaMetricsAtomHostSideTests",
"testTrackChangeEvent_text", new LogSessionIdListener());
Thread.sleep(AtomTestUtils.WAIT_TIME_LONG);
List<StatsLog.EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
assertThat(data.size()).isEqualTo(1);
assertThat(data.get(0).getAtom().hasMediaPlaybackTrackChanged()).isTrue();
AtomsProto.MediaPlaybackTrackChanged result = data.get(
0).getAtom().getMediaPlaybackTrackChanged();
assertThat(result.getTimeSincePlaybackCreatedMillis()).isEqualTo(37278L);
assertThat(result.getState().toString()).isEqualTo("ON");
assertThat(result.getReason().toString()).isEqualTo("REASON_MANUAL");
assertThat(result.getContainerMimeType()).isEqualTo("text/foo");
assertThat(result.getSampleMimeType()).isEqualTo("text/plain");
assertThat(result.getCodecName()).isEqualTo("codec_1");
assertThat(result.getBitrate()).isEqualTo(1024);
assertThat(result.getType().toString()).isEqualTo("TEXT");
assertThat(result.getLanguage()).isEqualTo("EN");
assertThat(result.getLanguageRegion()).isEqualTo("US");
}
@Test
public void testTrackChangeEvent_audio() throws Exception {
ConfigUtils.uploadConfigForPushedAtom(getDevice(), TEST_PKG,
AtomsProto.Atom.MEDIA_PLAYBACK_TRACK_CHANGED_FIELD_NUMBER);
runDeviceTests(getDevice(), TEST_PKG,
"android.media.metrics.cts.MediaMetricsAtomHostSideTests",
"testTrackChangeEvent_audio", new LogSessionIdListener());
Thread.sleep(AtomTestUtils.WAIT_TIME_LONG);
List<StatsLog.EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
assertThat(data.size()).isEqualTo(1);
assertThat(data.get(0).getAtom().hasMediaPlaybackTrackChanged()).isTrue();
AtomsProto.MediaPlaybackTrackChanged result = data.get(
0).getAtom().getMediaPlaybackTrackChanged();
assertThat(result.getTimeSincePlaybackCreatedMillis()).isEqualTo(37278L);
assertThat(result.getState().toString()).isEqualTo("OFF");
assertThat(result.getReason().toString()).isEqualTo("REASON_INITIAL");
assertThat(result.getContainerMimeType()).isEqualTo("audio/foo");
assertThat(result.getSampleMimeType()).isEqualTo("audio/avc");
assertThat(result.getCodecName()).isEqualTo("codec_2");
assertThat(result.getBitrate()).isEqualTo(1025);
assertThat(result.getType().toString()).isEqualTo("AUDIO");
assertThat(result.getLanguage()).isEqualTo("EN");
assertThat(result.getLanguageRegion()).isEqualTo("US");
assertThat(result.getSampleRate()).isEqualTo(89);
assertThat(result.getChannelCount()).isEqualTo(3);
}
@Test
public void testTrackChangeEvent_video() throws Exception {
ConfigUtils.uploadConfigForPushedAtom(getDevice(), TEST_PKG,
AtomsProto.Atom.MEDIA_PLAYBACK_TRACK_CHANGED_FIELD_NUMBER);
runDeviceTests(getDevice(), TEST_PKG,
"android.media.metrics.cts.MediaMetricsAtomHostSideTests",
"testTrackChangeEvent_video", new LogSessionIdListener());
Thread.sleep(AtomTestUtils.WAIT_TIME_LONG);
List<StatsLog.EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
assertThat(data.size()).isEqualTo(1);
assertThat(data.get(0).getAtom().hasMediaPlaybackTrackChanged()).isTrue();
AtomsProto.MediaPlaybackTrackChanged result = data.get(
0).getAtom().getMediaPlaybackTrackChanged();
assertThat(result.getTimeSincePlaybackCreatedMillis()).isEqualTo(37278L);
assertThat(result.getState().toString()).isEqualTo("OFF");
assertThat(result.getReason().toString()).isEqualTo("REASON_INITIAL");
assertThat(result.getContainerMimeType()).isEqualTo("video/foo");
assertThat(result.getSampleMimeType()).isEqualTo("video/mpeg");
assertThat(result.getCodecName()).isEqualTo("codec_3");
assertThat(result.getBitrate()).isEqualTo(1025);
assertThat(result.getType().toString()).isEqualTo("VIDEO");
assertThat(result.getLanguage()).isEqualTo("EN");
assertThat(result.getLanguageRegion()).isEqualTo("US");
assertThat(result.getHeight()).isEqualTo(1080);
assertThat(result.getWidth()).isEqualTo(1440);
assertThat(result.getVideoFrameRate()).isEqualTo(60);
}
@Test
public void testNetworkEvent_default() throws Exception {
ConfigUtils.uploadConfigForPushedAtom(getDevice(), TEST_PKG,
AtomsProto.Atom.MEDIA_NETWORK_INFO_CHANGED_FIELD_NUMBER);
runDeviceTests(getDevice(), TEST_PKG,
"android.media.metrics.cts.MediaMetricsAtomHostSideTests",
"testNetworkEvent_default", new LogSessionIdListener());
Thread.sleep(AtomTestUtils.WAIT_TIME_LONG);
List<StatsLog.EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
assertThat(data.size()).isEqualTo(1);
assertThat(data.get(0).getAtom().hasMediaNetworkInfoChanged()).isTrue();
AtomsProto.MediaNetworkInfoChanged result = data.get(
0).getAtom().getMediaNetworkInfoChanged();
assertThat(result.getTimeSincePlaybackCreatedMillis()).isEqualTo(-1L);
assertThat(result.getType().toString()).isEqualTo("NETWORK_TYPE_UNKNOWN");
}
@Test
public void testNetworkEvent() throws Exception {
ConfigUtils.uploadConfigForPushedAtom(getDevice(), TEST_PKG,
AtomsProto.Atom.MEDIA_NETWORK_INFO_CHANGED_FIELD_NUMBER);
runDeviceTests(getDevice(), TEST_PKG,
"android.media.metrics.cts.MediaMetricsAtomHostSideTests", "testNetworkEvent",
new LogSessionIdListener());
Thread.sleep(AtomTestUtils.WAIT_TIME_LONG);
List<StatsLog.EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
assertThat(data.size()).isEqualTo(1);
assertThat(data.get(0).getAtom().hasMediaNetworkInfoChanged()).isTrue();
AtomsProto.MediaNetworkInfoChanged result = data.get(
0).getAtom().getMediaNetworkInfoChanged();
assertThat(result.getTimeSincePlaybackCreatedMillis()).isEqualTo(3032L);
assertThat(result.getType().toString()).isEqualTo("NETWORK_TYPE_WIFI");
}
@Test
public void testPlaybackMetrics_default() throws Exception {
ConfigUtils.uploadConfigForPushedAtom(getDevice(), TEST_PKG,
AtomsProto.Atom.MEDIAMETRICS_PLAYBACK_REPORTED_FIELD_NUMBER);
runDeviceTests(getDevice(), TEST_PKG,
"android.media.metrics.cts.MediaMetricsAtomHostSideTests",
"testPlaybackMetrics_default", new LogSessionIdListener());
Thread.sleep(AtomTestUtils.WAIT_TIME_LONG);
List<StatsLog.EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
int appUid = DeviceUtils.getAppUid(getDevice(), TEST_PKG);
assertThat(data.size()).isEqualTo(1);
assertThat(data.get(0).getAtom().hasMediametricsPlaybackReported()).isTrue();
AtomsProto.MediametricsPlaybackReported result = data.get(
0).getAtom().getMediametricsPlaybackReported();
assertThat(result.getUid()).isEqualTo(appUid);
assertThat(result.getMediaDurationMillis()).isEqualTo(-1L);
assertThat(result.getStreamSource().toString()).isEqualTo("STREAM_SOURCE_UNKNOWN");
assertThat(result.getStreamType().toString()).isEqualTo("STREAM_TYPE_UNKNOWN");
assertThat(result.getPlaybackType().toString()).isEqualTo("PLAYBACK_TYPE_UNKNOWN");
assertThat(result.getDrmType().toString()).isEqualTo("DRM_TYPE_NONE");
assertThat(result.getContentType().toString()).isEqualTo("CONTENT_TYPE_UNKNOWN");
assertThat(result.getPlayerName()).isEqualTo("");
assertThat(result.getPlayerVersion()).isEqualTo("");
assertThat(result.getVideoFramesPlayed()).isEqualTo(-1);
assertThat(result.getVideoFramesDropped()).isEqualTo(-1);
assertThat(result.getAudioUnderrunCount()).isEqualTo(-1);
assertThat(result.getNetworkBytesRead()).isEqualTo(-1);
assertThat(result.getLocalBytesRead()).isEqualTo(-1);
assertThat(result.getNetworkTransferDurationMillis()).isEqualTo(-1);
assertThat(result.getExperimentIds().getExperimentsList().size()).isEqualTo(0);
assertThat(result.getDrmSessionId().length()).isEqualTo(0);
}
@Test
public void testPlaybackMetrics() throws Exception {
ConfigUtils.uploadConfigForPushedAtom(getDevice(), TEST_PKG,
AtomsProto.Atom.MEDIAMETRICS_PLAYBACK_REPORTED_FIELD_NUMBER);
runDeviceTests(getDevice(), TEST_PKG,
"android.media.metrics.cts.MediaMetricsAtomHostSideTests", "testPlaybackMetrics",
new LogSessionIdListener());
Thread.sleep(AtomTestUtils.WAIT_TIME_LONG);
List<StatsLog.EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
int appUid = DeviceUtils.getAppUid(getDevice(), TEST_PKG);
assertThat(data.size()).isEqualTo(1);
assertThat(data.get(0).getAtom().hasMediametricsPlaybackReported()).isTrue();
AtomsProto.MediametricsPlaybackReported result = data.get(
0).getAtom().getMediametricsPlaybackReported();
assertThat(result.getUid()).isEqualTo(appUid);
assertThat(result.getMediaDurationMillis()).isEqualTo(233L);
assertThat(result.getStreamSource().toString()).isEqualTo("STREAM_SOURCE_NETWORK");
assertThat(result.getStreamType().toString()).isEqualTo("STREAM_TYPE_OTHER");
assertThat(result.getPlaybackType().toString()).isEqualTo("PLAYBACK_TYPE_LIVE");
assertThat(result.getDrmType().toString()).isEqualTo("DRM_TYPE_WV_L1");
assertThat(result.getContentType().toString()).isEqualTo("CONTENT_TYPE_MAIN");
assertThat(result.getPlayerName()).isEqualTo("ExoPlayer");
assertThat(result.getPlayerVersion()).isEqualTo("1.01x");
assertThat(result.getVideoFramesPlayed()).isEqualTo(1024);
assertThat(result.getVideoFramesDropped()).isEqualTo(32);
assertThat(result.getAudioUnderrunCount()).isEqualTo(22);
assertThat(result.getNetworkBytesRead()).isEqualTo(102400);
assertThat(result.getLocalBytesRead()).isEqualTo(2000);
assertThat(result.getNetworkTransferDurationMillis()).isEqualTo(6000);
// TODO: fix missing experiment ID impl
assertThat(result.getExperimentIds()).isNotEqualTo(null);
// TODO: needs Base64 decoders to verify the data
assertThat(result.getDrmSessionId()).isNotEqualTo(null);
}
@Test
public void testSessionId() throws Exception {
ConfigUtils.uploadConfigForPushedAtom(getDevice(), TEST_PKG,
AtomsProto.Atom.MEDIAMETRICS_PLAYBACK_REPORTED_FIELD_NUMBER);
runDeviceTests(getDevice(), TEST_PKG,
"android.media.metrics.cts.MediaMetricsAtomHostSideTests", "testSessionId",
new LogSessionIdListener());
Thread.sleep(AtomTestUtils.WAIT_TIME_LONG);
List<StatsLog.EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
assertThat(data).isEmpty();
}
@Test
public void testRecordingSession() throws Exception {
ConfigUtils.uploadConfigForPushedAtom(getDevice(), TEST_PKG,
AtomsProto.Atom.MEDIAMETRICS_PLAYBACK_REPORTED_FIELD_NUMBER);
runDeviceTests(getDevice(), TEST_PKG,
"android.media.metrics.cts.MediaMetricsAtomHostSideTests", "testRecordingSession",
new LogSessionIdListener());
Thread.sleep(AtomTestUtils.WAIT_TIME_LONG);
List<StatsLog.EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
assertThat(data).isEmpty();
}
@Test
public void testEditingSession() throws Exception {
ConfigUtils.uploadConfigForPushedAtom(getDevice(), TEST_PKG,
AtomsProto.Atom.MEDIAMETRICS_PLAYBACK_REPORTED_FIELD_NUMBER);
runDeviceTests(getDevice(), TEST_PKG,
"android.media.metrics.cts.MediaMetricsAtomHostSideTests", "testEditingSession",
new LogSessionIdListener());
Thread.sleep(AtomTestUtils.WAIT_TIME_LONG);
List<StatsLog.EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
assertThat(data).isEmpty();
}
@Test
public void testTranscodingSession() throws Exception {
ConfigUtils.uploadConfigForPushedAtom(getDevice(), TEST_PKG,
AtomsProto.Atom.MEDIAMETRICS_PLAYBACK_REPORTED_FIELD_NUMBER);
runDeviceTests(getDevice(), TEST_PKG,
"android.media.metrics.cts.MediaMetricsAtomHostSideTests",
"testTranscodingSession", new LogSessionIdListener());
Thread.sleep(AtomTestUtils.WAIT_TIME_LONG);
List<StatsLog.EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
assertThat(data).isEmpty();
}
@Test
public void testBundleSession() throws Exception {
ConfigUtils.uploadConfigForPushedAtom(getDevice(), TEST_PKG,
AtomsProto.Atom.MEDIAMETRICS_PLAYBACK_REPORTED_FIELD_NUMBER);
runDeviceTests(getDevice(), TEST_PKG,
"android.media.metrics.cts.MediaMetricsAtomHostSideTests", "testBundleSession",
new LogSessionIdListener());
Thread.sleep(AtomTestUtils.WAIT_TIME_LONG);
List<StatsLog.EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
assertThat(data).isEmpty();
}
@Test
public void testAppBlocklist() throws Exception {
ConfigUtils.uploadConfigForPushedAtom(getDevice(), TEST_PKG,
AtomsProto.Atom.MEDIA_PLAYBACK_STATE_CHANGED_FIELD_NUMBER);
LogSessionIdListener listener = new LogSessionIdListener();
runDeviceTests(getDevice(),
TEST_PKG,
"android.media.metrics.cts.MediaMetricsAtomHostSideTests", "testAppBlocklist",
listener);
String logSessionId = listener.getLogSessionId();
Thread.sleep(AtomTestUtils.WAIT_TIME_LONG);
assertWithMessage("log session id").that(logSessionId).isNotEmpty();
List<StatsLog.EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
List<AtomsProto.MediametricsPlaybackReported> playbackReportedList = toMyAtoms(data,
AtomsProto.Atom::getMediametricsPlaybackReported);
assertThat(playbackReportedList).comparingElementsUsing(Correspondence.transforming(
AtomsProto.MediametricsPlaybackReported::getLogSessionId,
"getLogSessionId")).doesNotContain(logSessionId);
}
@Test
public void testAttributionBlocklist() throws Exception {
ConfigUtils.uploadConfigForPushedAtom(getDevice(), TEST_PKG,
AtomsProto.Atom.MEDIAMETRICS_PLAYBACK_REPORTED_FIELD_NUMBER);
LogSessionIdListener listener = new LogSessionIdListener();
runDeviceTests(getDevice(), TEST_PKG,
"android.media.metrics.cts.MediaMetricsAtomHostSideTests",
"testAttributionBlocklist", listener);
String logSessionId = listener.getLogSessionId();
Thread.sleep(AtomTestUtils.WAIT_TIME_LONG);
List<StatsLog.EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
assertWithMessage("log session id").that(logSessionId).isNotEmpty();
List<AtomsProto.MediametricsPlaybackReported> playbackReportedList = toMyAtoms(data,
AtomsProto.Atom::getMediametricsPlaybackReported);
assertThat(playbackReportedList).comparingElementsUsing(Correspondence.transforming(
AtomsProto.MediametricsPlaybackReported::getLogSessionId,
"getLogSessionId")).contains(logSessionId);
AtomsProto.MediametricsPlaybackReported result = playbackReportedList.stream().filter(
a -> a.getLogSessionId().equals(logSessionId)).findFirst().orElseThrow();
assertThat(result.getUid()).isEqualTo(0); // UID is not logged. Should be 0.
assertThat(result.getMediaDurationMillis()).isEqualTo(233L);
assertThat(result.getStreamSource().toString()).isEqualTo("STREAM_SOURCE_NETWORK");
assertThat(result.getStreamType().toString()).isEqualTo("STREAM_TYPE_OTHER");
assertThat(result.getPlaybackType().toString()).isEqualTo("PLAYBACK_TYPE_LIVE");
assertThat(result.getDrmType().toString()).isEqualTo("DRM_TYPE_WV_L1");
assertThat(result.getContentType().toString()).isEqualTo("CONTENT_TYPE_MAIN");
assertThat(result.getPlayerName()).isEqualTo("ExoPlayer");
assertThat(result.getPlayerVersion()).isEqualTo("1.01x");
assertThat(result.getVideoFramesPlayed()).isEqualTo(1024);
assertThat(result.getVideoFramesDropped()).isEqualTo(32);
assertThat(result.getAudioUnderrunCount()).isEqualTo(22);
assertThat(result.getNetworkBytesRead()).isEqualTo(102400);
assertThat(result.getLocalBytesRead()).isEqualTo(2000);
assertThat(result.getNetworkTransferDurationMillis()).isEqualTo(6000);
}
@Test
public void testAppAllowlist() throws Exception {
ConfigUtils.uploadConfigForPushedAtom(getDevice(), TEST_PKG,
AtomsProto.Atom.MEDIA_PLAYBACK_STATE_CHANGED_FIELD_NUMBER);
LogSessionIdListener listener = new LogSessionIdListener();
runDeviceTests(getDevice(), TEST_PKG,
"android.media.metrics.cts.MediaMetricsAtomHostSideTests",
"testAppAllowlist", listener);
String logSessionId = listener.getLogSessionId();
Thread.sleep(AtomTestUtils.WAIT_TIME_LONG);
List<StatsLog.EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
assertWithMessage("log session id").that(logSessionId).isNotEmpty();
List<AtomsProto.MediaPlaybackStateChanged> stateChangedList = toMyAtoms(data,
AtomsProto.Atom::getMediaPlaybackStateChanged);
assertThat(stateChangedList).comparingElementsUsing(
Correspondence.transforming(AtomsProto.MediaPlaybackStateChanged::getLogSessionId,
"getLogSessionId")).contains(logSessionId);
AtomsProto.MediaPlaybackStateChanged result = stateChangedList.stream().filter(
a -> a.getLogSessionId().equals(logSessionId)).findFirst().orElseThrow();
assertThat(result.getPlaybackState().toString()).isEqualTo("JOINING_FOREGROUND");
assertThat(result.getTimeSincePlaybackCreatedMillis()).isEqualTo(1763L);
}
@Test
public void testAttributionAllowlist() throws Exception {
ConfigUtils.uploadConfigForPushedAtom(getDevice(), TEST_PKG,
AtomsProto.Atom.MEDIAMETRICS_PLAYBACK_REPORTED_FIELD_NUMBER);
LogSessionIdListener listener = new LogSessionIdListener();
runDeviceTests(getDevice(), TEST_PKG,
"android.media.metrics.cts.MediaMetricsAtomHostSideTests",
"testAttributionAllowlist", listener);
String logSessionId = listener.getLogSessionId();
Thread.sleep(AtomTestUtils.WAIT_TIME_LONG);
List<StatsLog.EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
assertWithMessage("log session id").that(logSessionId).isNotEmpty();
List<AtomsProto.MediametricsPlaybackReported> playbackReportedList = toMyAtoms(data,
AtomsProto.Atom::getMediametricsPlaybackReported);
assertThat(playbackReportedList).comparingElementsUsing(Correspondence.transforming(
AtomsProto.MediametricsPlaybackReported::getLogSessionId,
"getLogSessionId")).contains(logSessionId);
AtomsProto.MediametricsPlaybackReported result = playbackReportedList.stream().filter(
a -> a.getLogSessionId().equals(logSessionId)).findFirst().orElseThrow();
assertThat(result.getUid()).isEqualTo(0); // UID is not logged. Should be 0.
assertThat(result.getMediaDurationMillis()).isEqualTo(233L);
assertThat(result.getStreamSource().toString()).isEqualTo("STREAM_SOURCE_NETWORK");
assertThat(result.getStreamType().toString()).isEqualTo("STREAM_TYPE_OTHER");
assertThat(result.getPlaybackType().toString()).isEqualTo("PLAYBACK_TYPE_LIVE");
assertThat(result.getDrmType().toString()).isEqualTo("DRM_TYPE_WV_L1");
assertThat(result.getContentType().toString()).isEqualTo("CONTENT_TYPE_MAIN");
assertThat(result.getPlayerName()).isEqualTo("ExoPlayer");
assertThat(result.getPlayerVersion()).isEqualTo("1.01x");
assertThat(result.getVideoFramesPlayed()).isEqualTo(1024);
assertThat(result.getVideoFramesDropped()).isEqualTo(32);
assertThat(result.getAudioUnderrunCount()).isEqualTo(22);
assertThat(result.getNetworkBytesRead()).isEqualTo(102400);
assertThat(result.getLocalBytesRead()).isEqualTo(2000);
assertThat(result.getNetworkTransferDurationMillis()).isEqualTo(6000);
}
private void validateAAudioStreamAtom(int direction) throws Exception {
Set<Integer> directionSet = new HashSet<>(Arrays.asList(direction));
List<Set<Integer>> directionList = Arrays.asList(directionSet);
List<StatsLog.EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
AtomTestUtils.assertStatesOccurredInOrder(directionList, data, 0,
atom -> atom.getMediametricsAaudiostreamReported().getDirection().getNumber());
for (StatsLog.EventMetricData event : data) {
AtomsProto.MediametricsAAudioStreamReported atom =
event.getAtom().getMediametricsAaudiostreamReported();
assertThat(atom.getBufferCapacity()).isGreaterThan(0);
assertThat(atom.getBufferCapacity()).isLessThan(MAX_BUFFER_CAPACITY);
assertThat(atom.getBufferSize()).isGreaterThan(0);
assertThat(atom.getBufferSize()).isAtMost(atom.getBufferCapacity());
assertThat(atom.getFramesPerBurst()).isGreaterThan(0);
assertThat(atom.getFramesPerBurst()).isLessThan(atom.getBufferCapacity());
}
}
private void runAAudioTestAndValidate(String requiredFeature, int direction,
String testFunctionName) throws Exception {
if (!DeviceUtils.hasFeature(getDevice(), requiredFeature)) {
return;
}
ConfigUtils.uploadConfigForPushedAtom(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
AtomsProto.Atom.MEDIAMETRICS_AAUDIOSTREAM_REPORTED_FIELD_NUMBER);
runDeviceTests(getDevice(), TEST_PKG,
"android.media.metrics.cts.MediaMetricsAtomHostSideTests", testFunctionName,
new LogSessionIdListener());
Thread.sleep(AtomTestUtils.WAIT_TIME_LONG);
validateAAudioStreamAtom(direction);
}
/**
* The test try to create and then close aaudio input stream with low latency via media metrics
* atom host side test app on the DUT.
* After that, the event metric data for MediametricsAAudioStreamReported is pushed to verify
* the data is collected correctly.
*/
@Test
public void testAAudioLowLatencyInputStream() throws Exception {
runAAudioTestAndValidate(FEATURE_MICROPHONE,
AtomsProto.MediametricsAAudioStreamReported.Direction.DIRECTION_INPUT_VALUE,
"testAAudioLowLatencyInputStream");
}
/**
* The test try to create and then close aaudio output stream with low latency via media metrics
* atom host side test app on the DUT.
* After that, the event metric data for MediametricsAAudioStreamReported is pushed to verify
* the data is collected correctly.
*/
@Test
public void testAAudioLowLatencyOutputStream() throws Exception {
runAAudioTestAndValidate(FEATURE_AUDIO_OUTPUT,
AtomsProto.MediametricsAAudioStreamReported.Direction.DIRECTION_OUTPUT_VALUE,
"testAAudioLowLatencyOutputStream");
}
/**
* The test try to create and then close aaudio input stream with legacy path via media metrics
* atom host side test app on the DUT.
* After that, the event metric data for MediametricsAAudioStreamReported is pushed to verify
* the data is collected correctly.
*/
@Test
public void testAAudioLegacyInputStream() throws Exception {
runAAudioTestAndValidate(FEATURE_MICROPHONE,
AtomsProto.MediametricsAAudioStreamReported.Direction.DIRECTION_INPUT_VALUE,
"testAAudioLegacyInputStream");
}
/**
* The test try to create and then close aaudio output stream with legacy path via media metrics
* atom host side test app on the DUT.
* After that, the event metric data for MediametricsAAudioStreamReported is pushed to verify
* the data is collected correctly.
*/
@Test
public void testAAudioLegacyOutputStream() throws Exception {
runAAudioTestAndValidate(FEATURE_AUDIO_OUTPUT,
AtomsProto.MediametricsAAudioStreamReported.Direction.DIRECTION_OUTPUT_VALUE,
"testAAudioLegacyOutputStream");
}
private static <T> List<T> toMyAtoms(List<StatsLog.EventMetricData> data,
Function<AtomsProto.Atom, T> mapper) {
return data.stream().map(StatsLog.EventMetricData::getAtom).map(mapper).collect(
Collectors.toUnmodifiableList());
}
// TODO(b/265208340): update DeviceUtils to accept listeners.
/**
* Runs device side tests.
*
* @param device Can be retrieved by running getDevice() in a class that extends
* DeviceTestCase
* @param pkgName Test package name, such as "com.android.server.cts.statsdatom"
* @param testClassName Test class name which can either be a fully qualified name or "." + a
* class name; if null, all test in the package will be run
* @param testMethodName Test method name; if null, all tests in class or package will be run
* @return {@link TestRunResult} of this invocation
*/
@Nonnull
private static TestRunResult runDeviceTests(ITestDevice device, String pkgName,
@Nullable String testClassName, @Nullable String testMethodName,
LogSessionIdListener listener)
throws DeviceNotAvailableException {
if (testClassName != null && testClassName.startsWith(".")) {
testClassName = pkgName + testClassName;
}
RemoteAndroidTestRunner testRunner = new RemoteAndroidTestRunner(
pkgName, TEST_RUNNER, device.getIDevice());
if (testClassName != null && testMethodName != null) {
testRunner.setMethodName(testClassName, testMethodName);
} else if (testClassName != null) {
testRunner.setClassName(testClassName);
}
assertThat(device.runInstrumentationTests(testRunner, listener)).isTrue();
final TestRunResult result = listener.getCurrentRunResults();
if (result.isRunFailure()) {
throw new Error("Failed to successfully run device tests for "
+ result.getName() + ": " + result.getRunFailureMessage());
}
if (result.getNumTests() == 0) {
throw new Error("No tests were run on the device");
}
if (result.hasFailedTests()) {
StringBuilder errorBuilder = new StringBuilder("On-device tests failed:\n");
for (Map.Entry<TestDescription, TestResult> resultEntry :
result.getTestResults().entrySet()) {
if (!resultEntry.getValue().getStatus().equals(
com.android.ddmlib.testrunner.TestResult.TestStatus.PASSED)) {
errorBuilder.append(resultEntry.getKey().toString());
errorBuilder.append(":\n");
errorBuilder.append(resultEntry.getValue().getStackTrace());
}
}
throw new AssertionError(errorBuilder.toString());
}
return result;
}
private static final class LogSessionIdListener extends CollectingTestListener {
@Nullable
private String mLogSessionId;
@Nullable
public String getLogSessionId() {
return mLogSessionId;
}
@Override
public void testEnded(TestDescription test, long endTime,
HashMap<String, MetricMeasurement.Metric> testMetrics) {
super.testEnded(test, endTime, testMetrics);
LogUtil.CLog.i("testEnded MetricMeasurement.Metric " + testMetrics);
// TODO(b/265311058): use a common constant for metrics keys.
mLogSessionId = testMetrics.get("log_session_id").getMeasurements().getSingleString();
}
}
}