• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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 package com.google.android.exoplayer2.upstream;
17 
18 import android.net.Uri;
19 import androidx.annotation.IntDef;
20 import androidx.annotation.Nullable;
21 import com.google.android.exoplayer2.C;
22 import com.google.android.exoplayer2.util.Assertions;
23 import java.lang.annotation.Documented;
24 import java.lang.annotation.Retention;
25 import java.lang.annotation.RetentionPolicy;
26 import java.util.Collections;
27 import java.util.HashMap;
28 import java.util.Map;
29 
30 /**
31  * Defines a region of data.
32  */
33 public final class DataSpec {
34 
35   /**
36    * Builds {@link DataSpec} instances.
37    *
38    * <p>Use DataSpec#buildUpon() to obtain a builder representing an existing {@link DataSpec}.
39    */
40   public static final class Builder {
41 
42     @Nullable private Uri uri;
43     private long uriPositionOffset;
44     @HttpMethod private int httpMethod;
45     @Nullable private byte[] httpBody;
46     private Map<String, String> httpRequestHeaders;
47     private long position;
48     private long length;
49     @Nullable private String key;
50     @Flags private int flags;
51     @Nullable private Object customData;
52 
53     /** Creates a new instance with default values. */
Builder()54     public Builder() {
55       httpMethod = HTTP_METHOD_GET;
56       httpRequestHeaders = Collections.emptyMap();
57       length = C.LENGTH_UNSET;
58     }
59 
60     /**
61      * Creates a new instance to build upon the provided {@link DataSpec}.
62      *
63      * @param dataSpec The {@link DataSpec} to build upon.
64      */
Builder(DataSpec dataSpec)65     private Builder(DataSpec dataSpec) {
66       uri = dataSpec.uri;
67       uriPositionOffset = dataSpec.uriPositionOffset;
68       httpMethod = dataSpec.httpMethod;
69       httpBody = dataSpec.httpBody;
70       httpRequestHeaders = dataSpec.httpRequestHeaders;
71       position = dataSpec.position;
72       length = dataSpec.length;
73       key = dataSpec.key;
74       flags = dataSpec.flags;
75       customData = dataSpec.customData;
76     }
77 
78     /**
79      * Sets {@link DataSpec#uri}.
80      *
81      * @param uriString The {@link DataSpec#uri}.
82      * @return The builder.
83      */
setUri(String uriString)84     public Builder setUri(String uriString) {
85       this.uri = Uri.parse(uriString);
86       return this;
87     }
88 
89     /**
90      * Sets {@link DataSpec#uri}.
91      *
92      * @param uri The {@link DataSpec#uri}.
93      * @return The builder.
94      */
setUri(Uri uri)95     public Builder setUri(Uri uri) {
96       this.uri = uri;
97       return this;
98     }
99 
100     /**
101      * Sets the {@link DataSpec#uriPositionOffset}. The default value is 0.
102      *
103      * @param uriPositionOffset The {@link DataSpec#uriPositionOffset}.
104      * @return The builder.
105      */
setUriPositionOffset(long uriPositionOffset)106     public Builder setUriPositionOffset(long uriPositionOffset) {
107       this.uriPositionOffset = uriPositionOffset;
108       return this;
109     }
110 
111     /**
112      * Sets {@link DataSpec#httpMethod}. The default value is {@link #HTTP_METHOD_GET}.
113      *
114      * @param httpMethod The {@link DataSpec#httpMethod}.
115      * @return The builder.
116      */
setHttpMethod(@ttpMethod int httpMethod)117     public Builder setHttpMethod(@HttpMethod int httpMethod) {
118       this.httpMethod = httpMethod;
119       return this;
120     }
121 
122     /**
123      * Sets {@link DataSpec#httpBody}. The default value is {@code null}.
124      *
125      * @param httpBody The {@link DataSpec#httpBody}.
126      * @return The builder.
127      */
setHttpBody(@ullable byte[] httpBody)128     public Builder setHttpBody(@Nullable byte[] httpBody) {
129       this.httpBody = httpBody;
130       return this;
131     }
132 
133     /**
134      * Sets the {@link DataSpec#httpRequestHeaders}. The default value is an empty map.
135      *
136      * <p>Note: {@code Range}, {@code Accept-Encoding} and {@code User-Agent} should not be set with
137      * this method, since they are set directly by {@link HttpDataSource} implementations. See
138      * {@link DataSpec#httpRequestHeaders} for more details.
139      *
140      * @param httpRequestHeaders The {@link DataSpec#httpRequestHeaders}.
141      * @return The builder.
142      */
setHttpRequestHeaders(Map<String, String> httpRequestHeaders)143     public Builder setHttpRequestHeaders(Map<String, String> httpRequestHeaders) {
144       this.httpRequestHeaders = httpRequestHeaders;
145       return this;
146     }
147 
148     /**
149      * Sets the {@link DataSpec#position}. The default value is 0.
150      *
151      * @param position The {@link DataSpec#position}.
152      * @return The builder.
153      */
setPosition(long position)154     public Builder setPosition(long position) {
155       this.position = position;
156       return this;
157     }
158 
159     /**
160      * Sets the {@link DataSpec#length}. The default value is {@link C#LENGTH_UNSET}.
161      *
162      * @param length The {@link DataSpec#length}.
163      * @return The builder.
164      */
setLength(long length)165     public Builder setLength(long length) {
166       this.length = length;
167       return this;
168     }
169 
170     /**
171      * Sets the {@link DataSpec#key}. The default value is {@code null}.
172      *
173      * @param key The {@link DataSpec#key}.
174      * @return The builder.
175      */
setKey(@ullable String key)176     public Builder setKey(@Nullable String key) {
177       this.key = key;
178       return this;
179     }
180 
181     /**
182      * Sets the {@link DataSpec#flags}. The default value is 0.
183      *
184      * @param flags The {@link DataSpec#flags}.
185      * @return The builder.
186      */
setFlags(@lags int flags)187     public Builder setFlags(@Flags int flags) {
188       this.flags = flags;
189       return this;
190     }
191 
192     /**
193      * Sets the {@link DataSpec#customData}. The default value is {@code null}.
194      *
195      * @param customData The {@link DataSpec#customData}.
196      * @return The builder.
197      */
setCustomData(@ullable Object customData)198     public Builder setCustomData(@Nullable Object customData) {
199       this.customData = customData;
200       return this;
201     }
202 
203     /**
204      * Builds a {@link DataSpec} with the builder's current values.
205      *
206      * @return The build {@link DataSpec}.
207      * @throws IllegalStateException If {@link #setUri} has not been called.
208      */
build()209     public DataSpec build() {
210       Assertions.checkStateNotNull(uri, "The uri must be set.");
211       return new DataSpec(
212           uri,
213           uriPositionOffset,
214           httpMethod,
215           httpBody,
216           httpRequestHeaders,
217           position,
218           length,
219           key,
220           flags,
221           customData);
222     }
223   }
224 
225   /**
226    * The flags that apply to any request for data. Possible flag values are {@link
227    * #FLAG_ALLOW_GZIP}, {@link #FLAG_DONT_CACHE_IF_LENGTH_UNKNOWN}, {@link
228    * #FLAG_ALLOW_CACHE_FRAGMENTATION}, and {@link #FLAG_MIGHT_NOT_USE_FULL_NETWORK_SPEED}.
229    */
230   @Documented
231   @Retention(RetentionPolicy.SOURCE)
232   @IntDef(
233       flag = true,
234       value = {
235         FLAG_ALLOW_GZIP,
236         FLAG_DONT_CACHE_IF_LENGTH_UNKNOWN,
237         FLAG_ALLOW_CACHE_FRAGMENTATION,
238         FLAG_MIGHT_NOT_USE_FULL_NETWORK_SPEED
239       })
240   public @interface Flags {}
241   /**
242    * Allows an underlying network stack to request that the server use gzip compression.
243    *
244    * <p>Should not typically be set if the data being requested is already compressed (e.g. most
245    * audio and video requests). May be set when requesting other data.
246    *
247    * <p>When a {@link DataSource} is used to request data with this flag set, and if the {@link
248    * DataSource} does make a network request, then the value returned from {@link
249    * DataSource#open(DataSpec)} will typically be {@link C#LENGTH_UNSET}. The data read from {@link
250    * DataSource#read(byte[], int, int)} will be the decompressed data.
251    */
252   public static final int FLAG_ALLOW_GZIP = 1;
253   /** Prevents caching if the length cannot be resolved when the {@link DataSource} is opened. */
254   public static final int FLAG_DONT_CACHE_IF_LENGTH_UNKNOWN = 1 << 1;
255   /**
256    * Allows fragmentation of this request into multiple cache files, meaning a cache eviction policy
257    * will be able to evict individual fragments of the data. Depending on the cache implementation,
258    * setting this flag may also enable more concurrent access to the data (e.g. reading one fragment
259    * whilst writing another).
260    */
261   public static final int FLAG_ALLOW_CACHE_FRAGMENTATION = 1 << 2;
262   /**
263    * Indicates there are known external factors that might prevent the data from being loaded at
264    * full network speed (e.g. server throttling or unfinished live media chunks).
265    */
266   public static final int FLAG_MIGHT_NOT_USE_FULL_NETWORK_SPEED = 1 << 3;
267 
268   /**
269    * HTTP methods supported by ExoPlayer {@link HttpDataSource}s. One of {@link #HTTP_METHOD_GET},
270    * {@link #HTTP_METHOD_POST} or {@link #HTTP_METHOD_HEAD}.
271    */
272   @Documented
273   @Retention(RetentionPolicy.SOURCE)
274   @IntDef({HTTP_METHOD_GET, HTTP_METHOD_POST, HTTP_METHOD_HEAD})
275   public @interface HttpMethod {}
276   /** HTTP GET method. */
277   public static final int HTTP_METHOD_GET = 1;
278   /** HTTP POST method. */
279   public static final int HTTP_METHOD_POST = 2;
280   /** HTTP HEAD method. */
281   public static final int HTTP_METHOD_HEAD = 3;
282 
283   /**
284    * Returns an uppercase HTTP method name (e.g., "GET", "POST", "HEAD") corresponding to the given
285    * {@link HttpMethod}.
286    */
getStringForHttpMethod(@ttpMethod int httpMethod)287   public static String getStringForHttpMethod(@HttpMethod int httpMethod) {
288     switch (httpMethod) {
289       case HTTP_METHOD_GET:
290         return "GET";
291       case HTTP_METHOD_POST:
292         return "POST";
293       case HTTP_METHOD_HEAD:
294         return "HEAD";
295       default:
296         // Never happens.
297         throw new IllegalStateException();
298     }
299   }
300 
301   /** The {@link Uri} from which data should be read. */
302   public final Uri uri;
303 
304   /**
305    * The offset of the data located at {@link #uri} within an original resource.
306    *
307    * <p>Equal to 0 unless {@link #uri} provides access to a subset of an original resource. As an
308    * example, consider a resource that can be requested over the network and is 1000 bytes long. If
309    * {@link #uri} points to a local file that contains just bytes [200-300], then this field will be
310    * set to {@code 200}.
311    *
312    * <p>This field can be ignored except for in specific circumstances where the absolute position
313    * in the original resource is required in a {@link DataSource} chain. One example is when a
314    * {@link DataSource} needs to decrypt the content as it's read. In this case the absolute
315    * position in the original resource is typically needed to correctly initialize the decryption
316    * algorithm.
317    */
318   public final long uriPositionOffset;
319 
320   /**
321    * The HTTP method to use when requesting the data. This value will be ignored by non-HTTP {@link
322    * DataSource} implementations.
323    */
324   @HttpMethod public final int httpMethod;
325 
326   /**
327    * The HTTP request body, null otherwise. If the body is non-null, then {@code httpBody.length}
328    * will be non-zero.
329    */
330   @Nullable public final byte[] httpBody;
331 
332   /**
333    * Additional HTTP headers to use when requesting the data.
334    *
335    * <p>Note: This map is for additional headers specific to the data being requested. It does not
336    * include headers that are set directly by {@link HttpDataSource} implementations. In particular,
337    * this means the following headers are not included:
338    *
339    * <ul>
340    *   <li>{@code Range}: {@link HttpDataSource} implementations derive the {@code Range} header
341    *       from {@link #position} and {@link #length}.
342    *   <li>{@code Accept-Encoding}: {@link HttpDataSource} implementations derive the {@code
343    *       Accept-Encoding} header based on whether {@link #flags} includes {@link
344    *       #FLAG_ALLOW_GZIP}.
345    *   <li>{@code User-Agent}: {@link HttpDataSource} implementations set the {@code User-Agent}
346    *       header directly.
347    *   <li>Other headers set at the {@link HttpDataSource} layer. I.e., headers set using {@link
348    *       HttpDataSource#setRequestProperty(String, String)}, and using {@link
349    *       HttpDataSource.RequestProperties#set(String, String)} on the default properties obtained
350    *       from {@link HttpDataSource.Factory#getDefaultRequestProperties()}.
351    * </ul>
352    */
353   public final Map<String, String> httpRequestHeaders;
354 
355   /**
356    * The absolute position of the data in the full stream.
357    *
358    * @deprecated Use {@link #position} except for specific use cases where the absolute position
359    *     within the original resource is required within a {@link DataSource} chain. Where the
360    *     absolute position is required, use {@code uriPositionOffset + position}.
361    */
362   @Deprecated public final long absoluteStreamPosition;
363 
364   /** The position of the data when read from {@link #uri}. */
365   public final long position;
366 
367   /**
368    * The length of the data, or {@link C#LENGTH_UNSET}.
369    */
370   public final long length;
371 
372   /**
373    * A key that uniquely identifies the original stream. Used for cache indexing. May be null if the
374    * data spec is not intended to be used in conjunction with a cache.
375    */
376   @Nullable public final String key;
377 
378   /** Request {@link Flags flags}. */
379   @Flags public final int flags;
380 
381   /**
382    * Application specific data.
383    *
384    * <p>This field is intended for advanced use cases in which applications require the ability to
385    * attach custom data to {@link DataSpec} instances. The custom data should be immutable.
386    */
387   @Nullable public final Object customData;
388 
389   /**
390    * Constructs an instance.
391    *
392    * @param uri {@link #uri}.
393    */
DataSpec(Uri uri)394   public DataSpec(Uri uri) {
395     this(uri, /* position= */ 0, /* length= */ C.LENGTH_UNSET);
396   }
397 
398   /**
399    * Constructs an instance.
400    *
401    * @param uri {@link #uri}.
402    * @param position {@link #position}.
403    * @param length {@link #length}.
404    */
DataSpec(Uri uri, long position, long length)405   public DataSpec(Uri uri, long position, long length) {
406     this(
407         uri,
408         /* uriPositionOffset= */ 0,
409         HTTP_METHOD_GET,
410         /* httpBody= */ null,
411         /* httpRequestHeaders= */ Collections.emptyMap(),
412         position,
413         length,
414         /* key= */ null,
415         /* flags= */ 0,
416         /* customData= */ null);
417   }
418 
419   /**
420    * Constructs an instance.
421    *
422    * @deprecated Use {@link Builder}.
423    * @param uri {@link #uri}.
424    * @param flags {@link #flags}.
425    */
426   @SuppressWarnings("deprecation")
427   @Deprecated
DataSpec(Uri uri, @Flags int flags)428   public DataSpec(Uri uri, @Flags int flags) {
429     this(uri, /* position= */ 0, C.LENGTH_UNSET, /* key= */ null, flags);
430   }
431 
432   /**
433    * Constructs an instance.
434    *
435    * @deprecated Use {@link Builder}.
436    * @param uri {@link #uri}.
437    * @param position {@link #position}.
438    * @param length {@link #length}.
439    * @param key {@link #key}.
440    */
441   @SuppressWarnings("deprecation")
442   @Deprecated
DataSpec(Uri uri, long position, long length, @Nullable String key)443   public DataSpec(Uri uri, long position, long length, @Nullable String key) {
444     this(uri, position, position, length, key, /* flags= */ 0);
445   }
446 
447   /**
448    * Constructs an instance.
449    *
450    * @deprecated Use {@link Builder}.
451    * @param uri {@link #uri}.
452    * @param position {@link #position}.
453    * @param length {@link #length}.
454    * @param key {@link #key}.
455    * @param flags {@link #flags}.
456    */
457   @SuppressWarnings("deprecation")
458   @Deprecated
DataSpec(Uri uri, long position, long length, @Nullable String key, @Flags int flags)459   public DataSpec(Uri uri, long position, long length, @Nullable String key, @Flags int flags) {
460     this(uri, position, position, length, key, flags);
461   }
462 
463   /**
464    * Constructs an instance.
465    *
466    * @deprecated Use {@link Builder}.
467    * @param uri {@link #uri}.
468    * @param position {@link #position}, equal to {@link #position}.
469    * @param length {@link #length}.
470    * @param key {@link #key}.
471    * @param flags {@link #flags}.
472    * @param httpRequestHeaders {@link #httpRequestHeaders}
473    */
474   @SuppressWarnings("deprecation")
475   @Deprecated
DataSpec( Uri uri, long position, long length, @Nullable String key, @Flags int flags, Map<String, String> httpRequestHeaders)476   public DataSpec(
477       Uri uri,
478       long position,
479       long length,
480       @Nullable String key,
481       @Flags int flags,
482       Map<String, String> httpRequestHeaders) {
483     this(
484         uri,
485         HTTP_METHOD_GET,
486         /* httpBody= */ null,
487         position,
488         position,
489         length,
490         key,
491         flags,
492         httpRequestHeaders);
493   }
494 
495   /**
496    * Constructs an instance where {@link #uriPositionOffset} may be non-zero.
497    *
498    * @deprecated Use {@link Builder}.
499    * @param uri {@link #uri}.
500    * @param absoluteStreamPosition The sum of {@link #uriPositionOffset} and {@link #position}.
501    * @param position {@link #position}.
502    * @param length {@link #length}.
503    * @param key {@link #key}.
504    * @param flags {@link #flags}.
505    */
506   @SuppressWarnings("deprecation")
507   @Deprecated
DataSpec( Uri uri, long absoluteStreamPosition, long position, long length, @Nullable String key, @Flags int flags)508   public DataSpec(
509       Uri uri,
510       long absoluteStreamPosition,
511       long position,
512       long length,
513       @Nullable String key,
514       @Flags int flags) {
515     this(uri, /* postBody= */ null, absoluteStreamPosition, position, length, key, flags);
516   }
517 
518   /**
519    * Construct a instance where {@link #uriPositionOffset} may be non-zero. The {@link #httpMethod}
520    * is inferred from {@code postBody}. If {@code postBody} is non-null then {@link #httpMethod} is
521    * set to {@link #HTTP_METHOD_POST}. If {@code postBody} is null then {@link #httpMethod} is set
522    * to {@link #HTTP_METHOD_GET}.
523    *
524    * @deprecated Use {@link Builder}.
525    * @param uri {@link #uri}.
526    * @param postBody {@link #httpBody} The body of the HTTP request, which is also used to infer the
527    *     {@link #httpMethod}.
528    * @param absoluteStreamPosition The sum of {@link #uriPositionOffset} and {@link #position}.
529    * @param position {@link #position}.
530    * @param length {@link #length}.
531    * @param key {@link #key}.
532    * @param flags {@link #flags}.
533    */
534   @SuppressWarnings("deprecation")
535   @Deprecated
DataSpec( Uri uri, @Nullable byte[] postBody, long absoluteStreamPosition, long position, long length, @Nullable String key, @Flags int flags)536   public DataSpec(
537       Uri uri,
538       @Nullable byte[] postBody,
539       long absoluteStreamPosition,
540       long position,
541       long length,
542       @Nullable String key,
543       @Flags int flags) {
544     this(
545         uri,
546         /* httpMethod= */ postBody != null ? HTTP_METHOD_POST : HTTP_METHOD_GET,
547         /* httpBody= */ postBody,
548         absoluteStreamPosition,
549         position,
550         length,
551         key,
552         flags);
553   }
554 
555   /**
556    * Construct a instance where {@link #uriPositionOffset} may be non-zero.
557    *
558    * @deprecated Use {@link Builder}.
559    * @param uri {@link #uri}.
560    * @param httpMethod {@link #httpMethod}.
561    * @param httpBody {@link #httpBody}.
562    * @param absoluteStreamPosition The sum of {@link #uriPositionOffset} and {@link #position}.
563    * @param position {@link #position}.
564    * @param length {@link #length}.
565    * @param key {@link #key}.
566    * @param flags {@link #flags}.
567    */
568   @SuppressWarnings("deprecation")
569   @Deprecated
DataSpec( Uri uri, @HttpMethod int httpMethod, @Nullable byte[] httpBody, long absoluteStreamPosition, long position, long length, @Nullable String key, @Flags int flags)570   public DataSpec(
571       Uri uri,
572       @HttpMethod int httpMethod,
573       @Nullable byte[] httpBody,
574       long absoluteStreamPosition,
575       long position,
576       long length,
577       @Nullable String key,
578       @Flags int flags) {
579     this(
580         uri,
581         httpMethod,
582         httpBody,
583         absoluteStreamPosition,
584         position,
585         length,
586         key,
587         flags,
588         /* httpRequestHeaders= */ Collections.emptyMap());
589   }
590 
591   /**
592    * Construct a instance where {@link #uriPositionOffset} may be non-zero.
593    *
594    * @deprecated Use {@link Builder}.
595    * @param uri {@link #uri}.
596    * @param httpMethod {@link #httpMethod}.
597    * @param httpBody {@link #httpBody}.
598    * @param absoluteStreamPosition The sum of {@link #uriPositionOffset} and {@link #position}.
599    * @param position {@link #position}.
600    * @param length {@link #length}.
601    * @param key {@link #key}.
602    * @param flags {@link #flags}.
603    * @param httpRequestHeaders {@link #httpRequestHeaders}.
604    */
605   @Deprecated
DataSpec( Uri uri, @HttpMethod int httpMethod, @Nullable byte[] httpBody, long absoluteStreamPosition, long position, long length, @Nullable String key, @Flags int flags, Map<String, String> httpRequestHeaders)606   public DataSpec(
607       Uri uri,
608       @HttpMethod int httpMethod,
609       @Nullable byte[] httpBody,
610       long absoluteStreamPosition,
611       long position,
612       long length,
613       @Nullable String key,
614       @Flags int flags,
615       Map<String, String> httpRequestHeaders) {
616     this(
617         uri,
618         /* uriPositionOffset= */ absoluteStreamPosition - position,
619         httpMethod,
620         httpBody,
621         httpRequestHeaders,
622         position,
623         length,
624         key,
625         flags,
626         /* customData= */ null);
627   }
628 
629   @SuppressWarnings("deprecation")
DataSpec( Uri uri, long uriPositionOffset, @HttpMethod int httpMethod, @Nullable byte[] httpBody, Map<String, String> httpRequestHeaders, long position, long length, @Nullable String key, @Flags int flags, @Nullable Object customData)630   private DataSpec(
631       Uri uri,
632       long uriPositionOffset,
633       @HttpMethod int httpMethod,
634       @Nullable byte[] httpBody,
635       Map<String, String> httpRequestHeaders,
636       long position,
637       long length,
638       @Nullable String key,
639       @Flags int flags,
640       @Nullable Object customData) {
641     // TODO: Replace this assertion with a stricter one checking "uriPositionOffset >= 0", after
642     // validating there are no violations in ExoPlayer and 1P apps.
643     Assertions.checkArgument(uriPositionOffset + position >= 0);
644     Assertions.checkArgument(position >= 0);
645     Assertions.checkArgument(length > 0 || length == C.LENGTH_UNSET);
646     this.uri = uri;
647     this.uriPositionOffset = uriPositionOffset;
648     this.httpMethod = httpMethod;
649     this.httpBody = httpBody != null && httpBody.length != 0 ? httpBody : null;
650     this.httpRequestHeaders = Collections.unmodifiableMap(new HashMap<>(httpRequestHeaders));
651     this.position = position;
652     this.absoluteStreamPosition = uriPositionOffset + position;
653     this.length = length;
654     this.key = key;
655     this.flags = flags;
656     this.customData = customData;
657   }
658 
659   /**
660    * Returns whether the given flag is set.
661    *
662    * @param flag Flag to be checked if it is set.
663    */
isFlagSet(@lags int flag)664   public boolean isFlagSet(@Flags int flag) {
665     return (this.flags & flag) == flag;
666   }
667 
668   /**
669    * Returns the uppercase HTTP method name (e.g., "GET", "POST", "HEAD") corresponding to the
670    * {@link #httpMethod}.
671    */
getHttpMethodString()672   public final String getHttpMethodString() {
673     return getStringForHttpMethod(httpMethod);
674   }
675 
676   /** Returns a {@link DataSpec.Builder} initialized with the values of this instance. */
buildUpon()677   public DataSpec.Builder buildUpon() {
678     return new Builder(this);
679   }
680 
681   /**
682    * Returns a data spec that represents a subrange of the data defined by this DataSpec. The
683    * subrange includes data from the offset up to the end of this DataSpec.
684    *
685    * @param offset The offset of the subrange.
686    * @return A data spec that represents a subrange of the data defined by this DataSpec.
687    */
subrange(long offset)688   public DataSpec subrange(long offset) {
689     return subrange(offset, length == C.LENGTH_UNSET ? C.LENGTH_UNSET : length - offset);
690   }
691 
692   /**
693    * Returns a data spec that represents a subrange of the data defined by this DataSpec.
694    *
695    * @param offset The offset of the subrange.
696    * @param length The length of the subrange.
697    * @return A data spec that represents a subrange of the data defined by this DataSpec.
698    */
subrange(long offset, long length)699   public DataSpec subrange(long offset, long length) {
700     if (offset == 0 && this.length == length) {
701       return this;
702     } else {
703       return new DataSpec(
704           uri,
705           uriPositionOffset,
706           httpMethod,
707           httpBody,
708           httpRequestHeaders,
709           position + offset,
710           length,
711           key,
712           flags,
713           customData);
714     }
715   }
716 
717   /**
718    * Returns a copy of this data spec with the specified Uri.
719    *
720    * @param uri The new source {@link Uri}.
721    * @return The copied data spec with the specified Uri.
722    */
withUri(Uri uri)723   public DataSpec withUri(Uri uri) {
724     return new DataSpec(
725         uri,
726         uriPositionOffset,
727         httpMethod,
728         httpBody,
729         httpRequestHeaders,
730         position,
731         length,
732         key,
733         flags,
734         customData);
735   }
736 
737   /**
738    * Returns a copy of this data spec with the specified HTTP request headers. Headers already in
739    * the data spec are not copied to the new instance.
740    *
741    * @param httpRequestHeaders The HTTP request headers.
742    * @return The copied data spec with the specified HTTP request headers.
743    */
withRequestHeaders(Map<String, String> httpRequestHeaders)744   public DataSpec withRequestHeaders(Map<String, String> httpRequestHeaders) {
745     return new DataSpec(
746         uri,
747         uriPositionOffset,
748         httpMethod,
749         httpBody,
750         httpRequestHeaders,
751         position,
752         length,
753         key,
754         flags,
755         customData);
756   }
757 
758   /**
759    * Returns a copy this data spec with additional HTTP request headers. Headers in {@code
760    * additionalHttpRequestHeaders} will overwrite any headers already in the data spec that have the
761    * same keys.
762    *
763    * @param additionalHttpRequestHeaders The additional HTTP request headers.
764    * @return The copied data spec with the additional HTTP request headers.
765    */
withAdditionalHeaders(Map<String, String> additionalHttpRequestHeaders)766   public DataSpec withAdditionalHeaders(Map<String, String> additionalHttpRequestHeaders) {
767     Map<String, String> httpRequestHeaders = new HashMap<>(this.httpRequestHeaders);
768     httpRequestHeaders.putAll(additionalHttpRequestHeaders);
769     return new DataSpec(
770         uri,
771         uriPositionOffset,
772         httpMethod,
773         httpBody,
774         httpRequestHeaders,
775         position,
776         length,
777         key,
778         flags,
779         customData);
780   }
781 
782   @Override
toString()783   public String toString() {
784     return "DataSpec["
785         + getHttpMethodString()
786         + " "
787         + uri
788         + ", "
789         + position
790         + ", "
791         + length
792         + ", "
793         + key
794         + ", "
795         + flags
796         + "]";
797   }
798 }
799