1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.media.metrics;
18 
19 import android.annotation.IntDef;
20 import android.annotation.IntRange;
21 import android.annotation.NonNull;
22 import android.annotation.Nullable;
23 import android.os.Bundle;
24 import android.os.Parcel;
25 import android.os.Parcelable;
26 
27 import com.android.internal.util.AnnotationValidations;
28 
29 import java.lang.annotation.Retention;
30 import java.lang.annotation.RetentionPolicy;
31 import java.util.ArrayList;
32 import java.util.Arrays;
33 import java.util.List;
34 import java.util.Objects;
35 
36 /**
37  * This class is used to store playback data.
38  */
39 public final class PlaybackMetrics implements Parcelable {
40     /** Unknown stream source. */
41     public static final int STREAM_SOURCE_UNKNOWN = 0;
42     /** Stream from network. */
43     public static final int STREAM_SOURCE_NETWORK = 1;
44     /** Stream from device. */
45     public static final int STREAM_SOURCE_DEVICE = 2;
46     /** Stream from more than one sources. */
47     public static final int STREAM_SOURCE_MIXED = 3;
48 
49     /** Unknown stream type. */
50     public static final int STREAM_TYPE_UNKNOWN = 0;
51     /** Other stream type. */
52     public static final int STREAM_TYPE_OTHER = 1;
53     /** Progressive stream type. */
54     public static final int STREAM_TYPE_PROGRESSIVE = 2;
55     /** DASH (Dynamic Adaptive Streaming over HTTP) stream type. */
56     public static final int STREAM_TYPE_DASH = 3;
57     /** HLS (HTTP Live Streaming) stream type. */
58     public static final int STREAM_TYPE_HLS = 4;
59     /** SS (HTTP Smooth Streaming) stream type. */
60     public static final int STREAM_TYPE_SS = 5;
61 
62     /** Unknown playback type. */
63     public static final int PLAYBACK_TYPE_UNKNOWN = 0;
64     /** VOD (Video on Demand) playback type. */
65     public static final int PLAYBACK_TYPE_VOD = 1;
66     /** Live playback type. */
67     public static final int PLAYBACK_TYPE_LIVE = 2;
68     /** Other playback type. */
69     public static final int PLAYBACK_TYPE_OTHER = 3;
70 
71     /** DRM is not used. */
72     public static final int DRM_TYPE_NONE = 0;
73     /** Other DRM type. */
74     public static final int DRM_TYPE_OTHER = 1;
75     /** Play ready DRM type. */
76     public static final int DRM_TYPE_PLAY_READY = 2;
77     /** Widevine L1 DRM type. */
78     public static final int DRM_TYPE_WIDEVINE_L1 = 3;
79     /** Widevine L3 DRM type. */
80     public static final int DRM_TYPE_WIDEVINE_L3 = 4;
81     /** Widevine L3 fallback DRM type. */
82     public static final int DRM_TYPE_WV_L3_FALLBACK = 5;
83     /** Clear key DRM type. */
84     public static final int DRM_TYPE_CLEARKEY = 6;
85 
86     /** Unknown content type. */
87     public static final int CONTENT_TYPE_UNKNOWN = 0;
88     /** Main contents. */
89     public static final int CONTENT_TYPE_MAIN = 1;
90     /** Advertisement contents. */
91     public static final int CONTENT_TYPE_AD = 2;
92     /** Other contents. */
93     public static final int CONTENT_TYPE_OTHER = 3;
94 
95 
96     /** @hide */
97     @IntDef(prefix = "STREAM_SOURCE_", value = {
98         STREAM_SOURCE_UNKNOWN,
99         STREAM_SOURCE_NETWORK,
100         STREAM_SOURCE_DEVICE,
101         STREAM_SOURCE_MIXED
102     })
103     @Retention(RetentionPolicy.SOURCE)
104     public @interface StreamSource {}
105 
106     /** @hide */
107     @IntDef(prefix = "STREAM_TYPE_", value = {
108         STREAM_TYPE_UNKNOWN,
109         STREAM_TYPE_OTHER,
110         STREAM_TYPE_PROGRESSIVE,
111         STREAM_TYPE_DASH,
112         STREAM_TYPE_HLS,
113         STREAM_TYPE_SS
114     })
115     @Retention(RetentionPolicy.SOURCE)
116     public @interface StreamType {}
117 
118     /** @hide */
119     @IntDef(prefix = "PLAYBACK_TYPE_", value = {
120         PLAYBACK_TYPE_UNKNOWN,
121         PLAYBACK_TYPE_VOD,
122         PLAYBACK_TYPE_LIVE,
123         PLAYBACK_TYPE_OTHER
124     })
125     @Retention(RetentionPolicy.SOURCE)
126     public @interface PlaybackType {}
127 
128     /** @hide */
129     @IntDef(prefix = "DRM_TYPE_", value = {
130         DRM_TYPE_NONE,
131         DRM_TYPE_OTHER,
132         DRM_TYPE_PLAY_READY,
133         DRM_TYPE_WIDEVINE_L1,
134         DRM_TYPE_WIDEVINE_L3,
135         DRM_TYPE_WV_L3_FALLBACK,
136         DRM_TYPE_CLEARKEY
137     })
138     @Retention(RetentionPolicy.SOURCE)
139     public @interface DrmType {}
140 
141     /** @hide */
142     @IntDef(prefix = "CONTENT_TYPE_", value = {
143         CONTENT_TYPE_UNKNOWN,
144         CONTENT_TYPE_MAIN,
145         CONTENT_TYPE_AD,
146         CONTENT_TYPE_OTHER
147     })
148     @Retention(RetentionPolicy.SOURCE)
149     public @interface ContentType {}
150 
151 
152 
153     private final long mMediaDurationMillis;
154     private final int mStreamSource;
155     private final int mStreamType;
156     private final int mPlaybackType;
157     private final int mDrmType;
158     private final int mContentType;
159     private final @Nullable String mPlayerName;
160     private final @Nullable String mPlayerVersion;
161     private final @NonNull long[] mExperimentIds;
162     private final int mVideoFramesPlayed;
163     private final int mVideoFramesDropped;
164     private final int mAudioUnderrunCount;
165     private final long mNetworkBytesRead;
166     private final long mLocalBytesRead;
167     private final long mNetworkTransferDurationMillis;
168     private final byte[] mDrmSessionId;
169     private final @NonNull Bundle mMetricsBundle;
170 
171     /**
172      * Creates a new PlaybackMetrics.
173      *
174      * @hide
175      */
PlaybackMetrics( long mediaDurationMillis, int streamSource, int streamType, int playbackType, int drmType, int contentType, @Nullable String playerName, @Nullable String playerVersion, @NonNull long[] experimentIds, int videoFramesPlayed, int videoFramesDropped, int audioUnderrunCount, long networkBytesRead, long localBytesRead, long networkTransferDurationMillis, byte[] drmSessionId, @NonNull Bundle extras)176     public PlaybackMetrics(
177             long mediaDurationMillis,
178             int streamSource,
179             int streamType,
180             int playbackType,
181             int drmType,
182             int contentType,
183             @Nullable String playerName,
184             @Nullable String playerVersion,
185             @NonNull long[] experimentIds,
186             int videoFramesPlayed,
187             int videoFramesDropped,
188             int audioUnderrunCount,
189             long networkBytesRead,
190             long localBytesRead,
191             long networkTransferDurationMillis,
192             byte[] drmSessionId,
193             @NonNull Bundle extras) {
194         this.mMediaDurationMillis = mediaDurationMillis;
195         this.mStreamSource = streamSource;
196         this.mStreamType = streamType;
197         this.mPlaybackType = playbackType;
198         this.mDrmType = drmType;
199         this.mContentType = contentType;
200         this.mPlayerName = playerName;
201         this.mPlayerVersion = playerVersion;
202         this.mExperimentIds = experimentIds;
203         AnnotationValidations.validate(NonNull.class, null, mExperimentIds);
204         this.mVideoFramesPlayed = videoFramesPlayed;
205         this.mVideoFramesDropped = videoFramesDropped;
206         this.mAudioUnderrunCount = audioUnderrunCount;
207         this.mNetworkBytesRead = networkBytesRead;
208         this.mLocalBytesRead = localBytesRead;
209         this.mNetworkTransferDurationMillis = networkTransferDurationMillis;
210         this.mDrmSessionId = drmSessionId;
211         this.mMetricsBundle = extras.deepCopy();
212     }
213 
214     /**
215      * Gets the media duration in milliseconds.
216      * <p>Media duration is the length of the media.
217      * @return the media duration in milliseconds, or -1 if unknown.
218      */
219     @IntRange(from = -1)
getMediaDurationMillis()220     public long getMediaDurationMillis() {
221         return mMediaDurationMillis;
222     }
223 
224     /**
225      * Gets stream source type.
226      */
227     @StreamSource
getStreamSource()228     public int getStreamSource() {
229         return mStreamSource;
230     }
231 
232     /**
233      * Gets stream type.
234      */
235     @StreamType
getStreamType()236     public int getStreamType() {
237         return mStreamType;
238     }
239 
240 
241     /**
242      * Gets playback type.
243      */
244     @PlaybackType
getPlaybackType()245     public int getPlaybackType() {
246         return mPlaybackType;
247     }
248 
249     /**
250      * Gets DRM type.
251      */
252     @DrmType
getDrmType()253     public int getDrmType() {
254         return mDrmType;
255     }
256 
257     /**
258      * Gets content type.
259      */
260     @ContentType
getContentType()261     public int getContentType() {
262         return mContentType;
263     }
264 
265     /**
266      * Gets player name.
267      */
getPlayerName()268     public @Nullable String getPlayerName() {
269         return mPlayerName;
270     }
271 
272     /**
273      * Gets player version.
274      */
getPlayerVersion()275     public @Nullable String getPlayerVersion() {
276         return mPlayerVersion;
277     }
278 
279     /**
280      * Gets experiment IDs.
281      */
getExperimentIds()282     public @NonNull long[] getExperimentIds() {
283         return Arrays.copyOf(mExperimentIds, mExperimentIds.length);
284     }
285 
286     /**
287      * Gets video frames played.
288      * @return the video frames played, or -1 if unknown.
289      */
290     @IntRange(from = -1, to = Integer.MAX_VALUE)
getVideoFramesPlayed()291     public int getVideoFramesPlayed() {
292         return mVideoFramesPlayed;
293     }
294 
295     /**
296      * Gets video frames dropped.
297      * @return the video frames dropped, or -1 if unknown.
298      */
299     @IntRange(from = -1, to = Integer.MAX_VALUE)
getVideoFramesDropped()300     public int getVideoFramesDropped() {
301         return mVideoFramesDropped;
302     }
303 
304     /**
305      * Gets audio underrun count.
306      * @return the audio underrun count, or -1 if unknown.
307      */
308     @IntRange(from = -1, to = Integer.MAX_VALUE)
getAudioUnderrunCount()309     public int getAudioUnderrunCount() {
310         return mAudioUnderrunCount;
311     }
312 
313     /**
314      * Gets number of network bytes read.
315      * @return the number of network bytes read, or -1 if unknown.
316      */
317     @IntRange(from = -1)
getNetworkBytesRead()318     public long getNetworkBytesRead() {
319         return mNetworkBytesRead;
320     }
321 
322     /**
323      * Gets number of local bytes read.
324      */
325     @IntRange(from = -1)
getLocalBytesRead()326     public long getLocalBytesRead() {
327         return mLocalBytesRead;
328     }
329 
330     /**
331      * Gets network transfer duration in milliseconds.
332      * <p>Total transfer time spent reading from the network in ms. For parallel requests, the
333      * overlapping time intervals are counted only once.
334      */
335     @IntRange(from = -1)
getNetworkTransferDurationMillis()336     public long getNetworkTransferDurationMillis() {
337         return mNetworkTransferDurationMillis;
338     }
339 
340     /**
341      * Gets DRM session ID.
342      */
343     @NonNull
getDrmSessionId()344     public byte[] getDrmSessionId() {
345         return mDrmSessionId;
346     }
347 
348     /**
349      * Gets metrics-related information that is not supported by dedicated methods.
350      * <p>It is intended to be used for backwards compatibility by the metrics infrastructure.
351      */
352     @NonNull
getMetricsBundle()353     public Bundle getMetricsBundle() {
354         return mMetricsBundle;
355     }
356 
357     @Override
toString()358     public String toString() {
359         return "PlaybackMetrics { "
360                 + "mediaDurationMillis = " + mMediaDurationMillis + ", "
361                 + "streamSource = " + mStreamSource + ", "
362                 + "streamType = " + mStreamType + ", "
363                 + "playbackType = " + mPlaybackType + ", "
364                 + "drmType = " + mDrmType + ", "
365                 + "contentType = " + mContentType + ", "
366                 + "playerName = " + mPlayerName + ", "
367                 + "playerVersion = " + mPlayerVersion + ", "
368                 + "experimentIds = " + Arrays.toString(mExperimentIds) + ", "
369                 + "videoFramesPlayed = " + mVideoFramesPlayed + ", "
370                 + "videoFramesDropped = " + mVideoFramesDropped + ", "
371                 + "audioUnderrunCount = " + mAudioUnderrunCount + ", "
372                 + "networkBytesRead = " + mNetworkBytesRead + ", "
373                 + "localBytesRead = " + mLocalBytesRead + ", "
374                 + "networkTransferDurationMillis = " + mNetworkTransferDurationMillis
375                 + "drmSessionId = " + Arrays.toString(mDrmSessionId)
376                 + " }";
377     }
378 
379     @Override
equals(@ullable Object o)380     public boolean equals(@Nullable Object o) {
381         if (this == o) return true;
382         if (o == null || getClass() != o.getClass()) return false;
383         PlaybackMetrics that = (PlaybackMetrics) o;
384         return mMediaDurationMillis == that.mMediaDurationMillis
385                 && mStreamSource == that.mStreamSource
386                 && mStreamType == that.mStreamType
387                 && mPlaybackType == that.mPlaybackType
388                 && mDrmType == that.mDrmType
389                 && mContentType == that.mContentType
390                 && Objects.equals(mPlayerName, that.mPlayerName)
391                 && Objects.equals(mPlayerVersion, that.mPlayerVersion)
392                 && Arrays.equals(mExperimentIds, that.mExperimentIds)
393                 && mVideoFramesPlayed == that.mVideoFramesPlayed
394                 && mVideoFramesDropped == that.mVideoFramesDropped
395                 && mAudioUnderrunCount == that.mAudioUnderrunCount
396                 && mNetworkBytesRead == that.mNetworkBytesRead
397                 && mLocalBytesRead == that.mLocalBytesRead
398                 && mNetworkTransferDurationMillis == that.mNetworkTransferDurationMillis
399                 && Arrays.equals(mDrmSessionId, that.mDrmSessionId);
400     }
401 
402     @Override
hashCode()403     public int hashCode() {
404         return Objects.hash(mMediaDurationMillis, mStreamSource, mStreamType, mPlaybackType,
405                 mDrmType, mContentType, mPlayerName, mPlayerVersion,
406                 Arrays.hashCode(mExperimentIds), mVideoFramesPlayed, mVideoFramesDropped,
407                 mAudioUnderrunCount, mNetworkBytesRead, mLocalBytesRead,
408                 mNetworkTransferDurationMillis, Arrays.hashCode(mDrmSessionId));
409     }
410 
411     @Override
writeToParcel(@onNull Parcel dest, int flags)412     public void writeToParcel(@NonNull Parcel dest, int flags) {
413         long flg = 0;
414         if (mPlayerName != null) flg |= 0x80;
415         if (mPlayerVersion != null) flg |= 0x100;
416         dest.writeLong(flg);
417         dest.writeLong(mMediaDurationMillis);
418         dest.writeInt(mStreamSource);
419         dest.writeInt(mStreamType);
420         dest.writeInt(mPlaybackType);
421         dest.writeInt(mDrmType);
422         dest.writeInt(mContentType);
423         if (mPlayerName != null) dest.writeString(mPlayerName);
424         if (mPlayerVersion != null) dest.writeString(mPlayerVersion);
425         dest.writeLongArray(mExperimentIds);
426         dest.writeInt(mVideoFramesPlayed);
427         dest.writeInt(mVideoFramesDropped);
428         dest.writeInt(mAudioUnderrunCount);
429         dest.writeLong(mNetworkBytesRead);
430         dest.writeLong(mLocalBytesRead);
431         dest.writeLong(mNetworkTransferDurationMillis);
432         dest.writeInt(mDrmSessionId.length);
433         dest.writeByteArray(mDrmSessionId);
434         dest.writeBundle(mMetricsBundle);
435     }
436 
437     @Override
describeContents()438     public int describeContents() {
439         return 0;
440     }
441 
442     /** @hide */
PlaybackMetrics(@onNull Parcel in)443     /* package-private */ PlaybackMetrics(@NonNull Parcel in) {
444         long flg = in.readLong();
445         long mediaDurationMillis = in.readLong();
446         int streamSource = in.readInt();
447         int streamType = in.readInt();
448         int playbackType = in.readInt();
449         int drmType = in.readInt();
450         int contentType = in.readInt();
451         String playerName = (flg & 0x80) == 0 ? null : in.readString();
452         String playerVersion = (flg & 0x100) == 0 ? null : in.readString();
453         long[] experimentIds = in.createLongArray();
454         int videoFramesPlayed = in.readInt();
455         int videoFramesDropped = in.readInt();
456         int audioUnderrunCount = in.readInt();
457         long networkBytesRead = in.readLong();
458         long localBytesRead = in.readLong();
459         long networkTransferDurationMillis = in.readLong();
460         int drmSessionIdLen = in.readInt();
461         byte[] drmSessionId = new byte[drmSessionIdLen];
462         in.readByteArray(drmSessionId);
463         Bundle extras = in.readBundle();
464 
465         this.mMediaDurationMillis = mediaDurationMillis;
466         this.mStreamSource = streamSource;
467         this.mStreamType = streamType;
468         this.mPlaybackType = playbackType;
469         this.mDrmType = drmType;
470         this.mContentType = contentType;
471         this.mPlayerName = playerName;
472         this.mPlayerVersion = playerVersion;
473         this.mExperimentIds = experimentIds;
474         AnnotationValidations.validate(NonNull.class, null, mExperimentIds);
475         this.mVideoFramesPlayed = videoFramesPlayed;
476         this.mVideoFramesDropped = videoFramesDropped;
477         this.mAudioUnderrunCount = audioUnderrunCount;
478         this.mNetworkBytesRead = networkBytesRead;
479         this.mLocalBytesRead = localBytesRead;
480         this.mNetworkTransferDurationMillis = networkTransferDurationMillis;
481         this.mDrmSessionId = drmSessionId;
482         this.mMetricsBundle = extras;
483     }
484 
485     public static final @NonNull Parcelable.Creator<PlaybackMetrics> CREATOR =
486             new Parcelable.Creator<PlaybackMetrics>() {
487         @Override
488         public PlaybackMetrics[] newArray(int size) {
489             return new PlaybackMetrics[size];
490         }
491 
492         @Override
493         public PlaybackMetrics createFromParcel(@NonNull Parcel in) {
494             return new PlaybackMetrics(in);
495         }
496     };
497 
498     /**
499      * A builder for {@link PlaybackMetrics}
500      */
501     public static final class Builder {
502 
503         private long mMediaDurationMillis = -1;
504         private int mStreamSource = STREAM_SOURCE_UNKNOWN;
505         private int mStreamType = STREAM_TYPE_UNKNOWN;
506         private int mPlaybackType = PLAYBACK_TYPE_UNKNOWN;
507         private int mDrmType = DRM_TYPE_NONE;
508         private int mContentType = CONTENT_TYPE_UNKNOWN;
509         private @Nullable String mPlayerName;
510         private @Nullable String mPlayerVersion;
511         private @NonNull List<Long> mExperimentIds = new ArrayList<>();
512         private int mVideoFramesPlayed = -1;
513         private int mVideoFramesDropped = -1;
514         private int mAudioUnderrunCount = -1;
515         private long mNetworkBytesRead = -1;
516         private long mLocalBytesRead = -1;
517         private long mNetworkTransferDurationMillis = -1;
518         private byte[] mDrmSessionId = new byte[0];
519         private Bundle mMetricsBundle = new Bundle();
520 
521         /**
522          * Creates a new Builder.
523          */
Builder()524         public Builder() {
525         }
526 
527         /**
528          * Sets the media duration in milliseconds.
529          * @param value the media duration in milliseconds. -1 indicates the value is unknown.
530          * @see #getMediaDurationMillis()
531          */
setMediaDurationMillis(@ntRangefrom = -1) long value)532         public @NonNull Builder setMediaDurationMillis(@IntRange(from = -1) long value) {
533             mMediaDurationMillis = value;
534             return this;
535         }
536 
537         /**
538          * Sets the stream source type.
539          */
setStreamSource(@treamSource int value)540         public @NonNull Builder setStreamSource(@StreamSource int value) {
541             mStreamSource = value;
542             return this;
543         }
544 
545         /**
546          * Sets the stream type.
547          */
setStreamType(@treamType int value)548         public @NonNull Builder setStreamType(@StreamType int value) {
549             mStreamType = value;
550             return this;
551         }
552 
553         /**
554          * Sets the playback type.
555          */
setPlaybackType(@laybackType int value)556         public @NonNull Builder setPlaybackType(@PlaybackType int value) {
557             mPlaybackType = value;
558             return this;
559         }
560 
561         /**
562          * Sets the DRM type.
563          */
setDrmType(@rmType int value)564         public @NonNull Builder setDrmType(@DrmType int value) {
565             mDrmType = value;
566             return this;
567         }
568 
569         /**
570          * Sets the content type.
571          */
setContentType(@ontentType int value)572         public @NonNull Builder setContentType(@ContentType int value) {
573             mContentType = value;
574             return this;
575         }
576 
577         /**
578          * Sets the player name.
579          */
setPlayerName(@onNull String value)580         public @NonNull Builder setPlayerName(@NonNull String value) {
581             mPlayerName = value;
582             return this;
583         }
584 
585         /**
586          * Sets the player version.
587          */
setPlayerVersion(@onNull String value)588         public @NonNull Builder setPlayerVersion(@NonNull String value) {
589             mPlayerVersion = value;
590             return this;
591         }
592 
593         /**
594          * Adds the experiment ID.
595          */
addExperimentId(long value)596         public @NonNull Builder addExperimentId(long value) {
597             mExperimentIds.add(value);
598             return this;
599         }
600 
601         /**
602          * Sets the video frames played.
603          * @param value the video frames played. -1 indicates the value is unknown.
604          */
setVideoFramesPlayed( @ntRangefrom = -1, to = Integer.MAX_VALUE) int value)605         public @NonNull Builder setVideoFramesPlayed(
606                 @IntRange(from = -1, to = Integer.MAX_VALUE) int value) {
607             mVideoFramesPlayed = value;
608             return this;
609         }
610 
611         /**
612          * Sets the video frames dropped.
613          * @param value the video frames dropped. -1 indicates the value is unknown.
614          */
setVideoFramesDropped( @ntRangefrom = -1, to = Integer.MAX_VALUE) int value)615         public @NonNull Builder setVideoFramesDropped(
616                 @IntRange(from = -1, to = Integer.MAX_VALUE) int value) {
617             mVideoFramesDropped = value;
618             return this;
619         }
620 
621         /**
622          * Sets the audio underrun count.
623          * @param value the audio underrun count. -1 indicates the value is unknown.
624          */
setAudioUnderrunCount( @ntRangefrom = -1, to = Integer.MAX_VALUE) int value)625         public @NonNull Builder setAudioUnderrunCount(
626                 @IntRange(from = -1, to = Integer.MAX_VALUE) int value) {
627             mAudioUnderrunCount = value;
628             return this;
629         }
630 
631         /**
632          * Sets the number of network bytes read.
633          * @param value the number of network bytes read. -1 indicates the value is unknown.
634          */
setNetworkBytesRead(@ntRangefrom = -1) long value)635         public @NonNull Builder setNetworkBytesRead(@IntRange(from = -1) long value) {
636             mNetworkBytesRead = value;
637             return this;
638         }
639 
640         /**
641          * Sets the number of local bytes read.
642          * @param value the number of local bytes read. -1 indicates the value is unknown.
643          */
setLocalBytesRead(@ntRangefrom = -1) long value)644         public @NonNull Builder setLocalBytesRead(@IntRange(from = -1) long value) {
645             mLocalBytesRead = value;
646             return this;
647         }
648 
649         /**
650          * Sets the network transfer duration in milliseconds.
651          * @param value the network transfer duration in milliseconds.
652          *              -1 indicates the value is unknown.
653          * @see #getNetworkTransferDurationMillis()
654          */
setNetworkTransferDurationMillis(@ntRangefrom = -1) long value)655         public @NonNull Builder setNetworkTransferDurationMillis(@IntRange(from = -1) long value) {
656             mNetworkTransferDurationMillis = value;
657             return this;
658         }
659 
660         /**
661          * Sets DRM session ID.
662          */
setDrmSessionId(@onNull byte[] drmSessionId)663         public @NonNull Builder setDrmSessionId(@NonNull byte[] drmSessionId) {
664             mDrmSessionId = drmSessionId;
665             return this;
666         }
667 
668         /**
669          * Sets metrics-related information that is not supported by dedicated
670          * methods.
671          * <p>It is intended to be used for backwards compatibility by the
672          * metrics infrastructure.
673          */
setMetricsBundle(@onNull Bundle metricsBundle)674         public @NonNull Builder setMetricsBundle(@NonNull Bundle metricsBundle) {
675             mMetricsBundle = metricsBundle;
676             return this;
677         }
678 
679         /** Builds the instance. This builder should not be touched after calling this! */
build()680         public @NonNull PlaybackMetrics build() {
681 
682             PlaybackMetrics o = new PlaybackMetrics(
683                     mMediaDurationMillis,
684                     mStreamSource,
685                     mStreamType,
686                     mPlaybackType,
687                     mDrmType,
688                     mContentType,
689                     mPlayerName,
690                     mPlayerVersion,
691                     idsToLongArray(),
692                     mVideoFramesPlayed,
693                     mVideoFramesDropped,
694                     mAudioUnderrunCount,
695                     mNetworkBytesRead,
696                     mLocalBytesRead,
697                     mNetworkTransferDurationMillis,
698                     mDrmSessionId,
699                     mMetricsBundle);
700             return o;
701         }
702 
idsToLongArray()703         private long[] idsToLongArray() {
704             long[] ids = new long[mExperimentIds.size()];
705             for (int i = 0; i < mExperimentIds.size(); i++) {
706                 ids[i] = mExperimentIds.get(i);
707             }
708             return ids;
709         }
710     }
711 }
712