• 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.util;
17 
18 import static android.content.Context.UI_MODE_SERVICE;
19 import static com.google.android.exoplayer2.Player.COMMAND_SEEK_BACK;
20 import static com.google.android.exoplayer2.Player.COMMAND_SEEK_FORWARD;
21 import static com.google.android.exoplayer2.Player.COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM;
22 import static com.google.android.exoplayer2.Player.COMMAND_SEEK_TO_DEFAULT_POSITION;
23 import static com.google.android.exoplayer2.Player.COMMAND_SEEK_TO_MEDIA_ITEM;
24 import static com.google.android.exoplayer2.Player.COMMAND_SEEK_TO_NEXT;
25 import static com.google.android.exoplayer2.Player.COMMAND_SEEK_TO_NEXT_MEDIA_ITEM;
26 import static com.google.android.exoplayer2.Player.COMMAND_SEEK_TO_PREVIOUS;
27 import static com.google.android.exoplayer2.Player.COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM;
28 import static com.google.android.exoplayer2.util.Assertions.checkNotNull;
29 import static java.lang.Math.abs;
30 import static java.lang.Math.max;
31 import static java.lang.Math.min;
32 
33 import android.Manifest.permission;
34 import android.annotation.SuppressLint;
35 import android.app.Activity;
36 import android.app.UiModeManager;
37 import android.content.ComponentName;
38 import android.content.Context;
39 import android.content.Intent;
40 import android.content.pm.PackageInfo;
41 import android.content.pm.PackageManager;
42 import android.content.pm.PackageManager.NameNotFoundException;
43 import android.content.res.Configuration;
44 import android.content.res.Resources;
45 import android.database.DatabaseUtils;
46 import android.database.sqlite.SQLiteDatabase;
47 import android.graphics.Point;
48 import android.hardware.display.DisplayManager;
49 import android.media.AudioFormat;
50 import android.media.AudioManager;
51 import android.media.MediaDrm;
52 import android.net.Uri;
53 import android.os.Build;
54 import android.os.Handler;
55 import android.os.Looper;
56 import android.os.Parcel;
57 import android.os.SystemClock;
58 import android.security.NetworkSecurityPolicy;
59 import android.telephony.TelephonyManager;
60 import android.text.TextUtils;
61 import android.util.Base64;
62 import android.util.SparseLongArray;
63 import android.view.Display;
64 import android.view.SurfaceView;
65 import android.view.WindowManager;
66 import androidx.annotation.Nullable;
67 import androidx.annotation.RequiresApi;
68 import com.google.android.exoplayer2.C;
69 import com.google.android.exoplayer2.C.ContentType;
70 import com.google.android.exoplayer2.ExoPlayerLibraryInfo;
71 import com.google.android.exoplayer2.Format;
72 import com.google.android.exoplayer2.MediaItem;
73 import com.google.android.exoplayer2.ParserException;
74 import com.google.android.exoplayer2.PlaybackException;
75 import com.google.android.exoplayer2.Player;
76 import com.google.android.exoplayer2.Player.Commands;
77 import com.google.common.base.Ascii;
78 import com.google.common.base.Charsets;
79 import java.io.ByteArrayOutputStream;
80 import java.io.Closeable;
81 import java.io.File;
82 import java.io.IOException;
83 import java.io.InputStream;
84 import java.lang.reflect.Method;
85 import java.math.BigDecimal;
86 import java.nio.ByteBuffer;
87 import java.nio.ByteOrder;
88 import java.util.ArrayDeque;
89 import java.util.Arrays;
90 import java.util.Calendar;
91 import java.util.Collections;
92 import java.util.Formatter;
93 import java.util.GregorianCalendar;
94 import java.util.HashMap;
95 import java.util.List;
96 import java.util.Locale;
97 import java.util.MissingResourceException;
98 import java.util.NoSuchElementException;
99 import java.util.TimeZone;
100 import java.util.UUID;
101 import java.util.concurrent.ExecutorService;
102 import java.util.concurrent.Executors;
103 import java.util.regex.Matcher;
104 import java.util.regex.Pattern;
105 import java.util.zip.DataFormatException;
106 import java.util.zip.GZIPOutputStream;
107 import java.util.zip.Inflater;
108 import org.checkerframework.checker.initialization.qual.UnknownInitialization;
109 import org.checkerframework.checker.nullness.compatqual.NullableType;
110 import org.checkerframework.checker.nullness.qual.EnsuresNonNull;
111 import org.checkerframework.checker.nullness.qual.PolyNull;
112 
113 /** Miscellaneous utility methods. */
114 public final class Util {
115 
116   /**
117    * Like {@link android.os.Build.VERSION#SDK_INT}, but in a place where it can be conveniently
118    * overridden for local testing.
119    */
120   public static final int SDK_INT = Build.VERSION.SDK_INT;
121 
122   /**
123    * Like {@link Build#DEVICE}, but in a place where it can be conveniently overridden for local
124    * testing.
125    */
126   public static final String DEVICE = Build.DEVICE;
127 
128   /**
129    * Like {@link Build#MANUFACTURER}, but in a place where it can be conveniently overridden for
130    * local testing.
131    */
132   public static final String MANUFACTURER = Build.MANUFACTURER;
133 
134   /**
135    * Like {@link Build#MODEL}, but in a place where it can be conveniently overridden for local
136    * testing.
137    */
138   public static final String MODEL = Build.MODEL;
139 
140   /** A concise description of the device that it can be useful to log for debugging purposes. */
141   public static final String DEVICE_DEBUG_INFO =
142       DEVICE + ", " + MODEL + ", " + MANUFACTURER + ", " + SDK_INT;
143 
144   /** An empty byte array. */
145   public static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
146 
147   private static final String TAG = "Util";
148   private static final Pattern XS_DATE_TIME_PATTERN =
149       Pattern.compile(
150           "(\\d\\d\\d\\d)\\-(\\d\\d)\\-(\\d\\d)[Tt]"
151               + "(\\d\\d):(\\d\\d):(\\d\\d)([\\.,](\\d+))?"
152               + "([Zz]|((\\+|\\-)(\\d?\\d):?(\\d\\d)))?");
153   private static final Pattern XS_DURATION_PATTERN =
154       Pattern.compile(
155           "^(-)?P(([0-9]*)Y)?(([0-9]*)M)?(([0-9]*)D)?"
156               + "(T(([0-9]*)H)?(([0-9]*)M)?(([0-9.]*)S)?)?$");
157   private static final Pattern ESCAPED_CHARACTER_PATTERN = Pattern.compile("%([A-Fa-f0-9]{2})");
158 
159   // https://docs.microsoft.com/en-us/azure/media-services/previous/media-services-deliver-content-overview#URLs.
160   private static final Pattern ISM_URL_PATTERN = Pattern.compile(".*\\.isml?(?:/(manifest(.*))?)?");
161   private static final String ISM_HLS_FORMAT_EXTENSION = "format=m3u8-aapl";
162   private static final String ISM_DASH_FORMAT_EXTENSION = "format=mpd-time-csf";
163 
164   // Replacement map of ISO language codes used for normalization.
165   @Nullable private static HashMap<String, String> languageTagReplacementMap;
166 
Util()167   private Util() {}
168 
169   /**
170    * Converts the entirety of an {@link InputStream} to a byte array.
171    *
172    * @param inputStream the {@link InputStream} to be read. The input stream is not closed by this
173    *     method.
174    * @return a byte array containing all of the inputStream's bytes.
175    * @throws IOException if an error occurs reading from the stream.
176    */
toByteArray(InputStream inputStream)177   public static byte[] toByteArray(InputStream inputStream) throws IOException {
178     byte[] buffer = new byte[1024 * 4];
179     ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
180     int bytesRead;
181     while ((bytesRead = inputStream.read(buffer)) != -1) {
182       outputStream.write(buffer, 0, bytesRead);
183     }
184     return outputStream.toByteArray();
185   }
186 
187   /**
188    * Calls {@link Context#startForegroundService(Intent)} if {@link #SDK_INT} is 26 or higher, or
189    * {@link Context#startService(Intent)} otherwise.
190    *
191    * @param context The context to call.
192    * @param intent The intent to pass to the called method.
193    * @return The result of the called method.
194    */
195   @Nullable
startForegroundService(Context context, Intent intent)196   public static ComponentName startForegroundService(Context context, Intent intent) {
197     if (Util.SDK_INT >= 26) {
198       return context.startForegroundService(intent);
199     } else {
200       return context.startService(intent);
201     }
202   }
203 
204   /**
205    * Checks whether it's necessary to request the {@link permission#READ_EXTERNAL_STORAGE}
206    * permission read the specified {@link Uri}s, requesting the permission if necessary.
207    *
208    * @param activity The host activity for checking and requesting the permission.
209    * @param uris {@link Uri}s that may require {@link permission#READ_EXTERNAL_STORAGE} to read.
210    * @return Whether a permission request was made.
211    */
maybeRequestReadExternalStoragePermission(Activity activity, Uri... uris)212   public static boolean maybeRequestReadExternalStoragePermission(Activity activity, Uri... uris) {
213     if (Util.SDK_INT < 23) {
214       return false;
215     }
216     for (Uri uri : uris) {
217       if (isLocalFileUri(uri)) {
218         return requestExternalStoragePermission(activity);
219       }
220     }
221     return false;
222   }
223 
224   /**
225    * Checks whether it's necessary to request the {@link permission#READ_EXTERNAL_STORAGE}
226    * permission for the specified {@link MediaItem media items}, requesting the permission if
227    * necessary.
228    *
229    * @param activity The host activity for checking and requesting the permission.
230    * @param mediaItems {@link MediaItem Media items}s that may require {@link
231    *     permission#READ_EXTERNAL_STORAGE} to read.
232    * @return Whether a permission request was made.
233    */
maybeRequestReadExternalStoragePermission( Activity activity, MediaItem... mediaItems)234   public static boolean maybeRequestReadExternalStoragePermission(
235       Activity activity, MediaItem... mediaItems) {
236     if (Util.SDK_INT < 23) {
237       return false;
238     }
239     for (MediaItem mediaItem : mediaItems) {
240       if (mediaItem.localConfiguration == null) {
241         continue;
242       }
243       if (isLocalFileUri(mediaItem.localConfiguration.uri)) {
244         return requestExternalStoragePermission(activity);
245       }
246       for (int i = 0; i < mediaItem.localConfiguration.subtitleConfigurations.size(); i++) {
247         if (isLocalFileUri(mediaItem.localConfiguration.subtitleConfigurations.get(i).uri)) {
248           return requestExternalStoragePermission(activity);
249         }
250       }
251     }
252     return false;
253   }
254 
255   /**
256    * Returns whether it may be possible to load the URIs of the given media items based on the
257    * network security policy's cleartext traffic permissions.
258    *
259    * @param mediaItems A list of {@link MediaItem media items}.
260    * @return Whether it may be possible to load the URIs of the given media items.
261    */
checkCleartextTrafficPermitted(MediaItem... mediaItems)262   public static boolean checkCleartextTrafficPermitted(MediaItem... mediaItems) {
263     if (Util.SDK_INT < 24) {
264       // We assume cleartext traffic is permitted.
265       return true;
266     }
267     for (MediaItem mediaItem : mediaItems) {
268       if (mediaItem.localConfiguration == null) {
269         continue;
270       }
271       if (isTrafficRestricted(mediaItem.localConfiguration.uri)) {
272         return false;
273       }
274       for (int i = 0; i < mediaItem.localConfiguration.subtitleConfigurations.size(); i++) {
275         if (isTrafficRestricted(mediaItem.localConfiguration.subtitleConfigurations.get(i).uri)) {
276           return false;
277         }
278       }
279     }
280     return true;
281   }
282 
283   /**
284    * Returns true if the URI is a path to a local file or a reference to a local file.
285    *
286    * @param uri The uri to test.
287    */
isLocalFileUri(Uri uri)288   public static boolean isLocalFileUri(Uri uri) {
289     String scheme = uri.getScheme();
290     return TextUtils.isEmpty(scheme) || "file".equals(scheme);
291   }
292 
293   /**
294    * Tests two objects for {@link Object#equals(Object)} equality, handling the case where one or
295    * both may be null.
296    *
297    * @param o1 The first object.
298    * @param o2 The second object.
299    * @return {@code o1 == null ? o2 == null : o1.equals(o2)}.
300    */
areEqual(@ullable Object o1, @Nullable Object o2)301   public static boolean areEqual(@Nullable Object o1, @Nullable Object o2) {
302     return o1 == null ? o2 == null : o1.equals(o2);
303   }
304 
305   /**
306    * Tests whether an {@code items} array contains an object equal to {@code item}, according to
307    * {@link Object#equals(Object)}.
308    *
309    * <p>If {@code item} is null then true is returned if and only if {@code items} contains null.
310    *
311    * @param items The array of items to search.
312    * @param item The item to search for.
313    * @return True if the array contains an object equal to the item being searched for.
314    */
contains(@ullableType Object[] items, @Nullable Object item)315   public static boolean contains(@NullableType Object[] items, @Nullable Object item) {
316     for (Object arrayItem : items) {
317       if (areEqual(arrayItem, item)) {
318         return true;
319       }
320     }
321     return false;
322   }
323 
324   /**
325    * Removes an indexed range from a List.
326    *
327    * <p>Does nothing if the provided range is valid and {@code fromIndex == toIndex}.
328    *
329    * @param list The List to remove the range from.
330    * @param fromIndex The first index to be removed (inclusive).
331    * @param toIndex The last index to be removed (exclusive).
332    * @throws IllegalArgumentException If {@code fromIndex} &lt; 0, {@code toIndex} &gt; {@code
333    *     list.size()}, or {@code fromIndex} &gt; {@code toIndex}.
334    */
removeRange(List<T> list, int fromIndex, int toIndex)335   public static <T> void removeRange(List<T> list, int fromIndex, int toIndex) {
336     if (fromIndex < 0 || toIndex > list.size() || fromIndex > toIndex) {
337       throw new IllegalArgumentException();
338     } else if (fromIndex != toIndex) {
339       // Checking index inequality prevents an unnecessary allocation.
340       list.subList(fromIndex, toIndex).clear();
341     }
342   }
343 
344   /**
345    * Casts a nullable variable to a non-null variable without runtime null check.
346    *
347    * <p>Use {@link Assertions#checkNotNull(Object)} to throw if the value is null.
348    */
349   @SuppressWarnings({"nullness:contracts.postcondition", "nullness:return"})
350   @EnsuresNonNull("#1")
castNonNull(@ullable T value)351   public static <T> T castNonNull(@Nullable T value) {
352     return value;
353   }
354 
355   /** Casts a nullable type array to a non-null type array without runtime null check. */
356   @SuppressWarnings({"nullness:contracts.postcondition", "nullness:return"})
357   @EnsuresNonNull("#1")
castNonNullTypeArray(@ullableType T[] value)358   public static <T> T[] castNonNullTypeArray(@NullableType T[] value) {
359     return value;
360   }
361 
362   /**
363    * Copies and optionally truncates an array. Prevents null array elements created by {@link
364    * Arrays#copyOf(Object[], int)} by ensuring the new length does not exceed the current length.
365    *
366    * @param input The input array.
367    * @param length The output array length. Must be less or equal to the length of the input array.
368    * @return The copied array.
369    */
370   @SuppressWarnings({"nullness:argument", "nullness:return"})
nullSafeArrayCopy(T[] input, int length)371   public static <T> T[] nullSafeArrayCopy(T[] input, int length) {
372     Assertions.checkArgument(length <= input.length);
373     return Arrays.copyOf(input, length);
374   }
375 
376   /**
377    * Copies a subset of an array.
378    *
379    * @param input The input array.
380    * @param from The start the range to be copied, inclusive
381    * @param to The end of the range to be copied, exclusive.
382    * @return The copied array.
383    */
384   @SuppressWarnings({"nullness:argument", "nullness:return"})
nullSafeArrayCopyOfRange(T[] input, int from, int to)385   public static <T> T[] nullSafeArrayCopyOfRange(T[] input, int from, int to) {
386     Assertions.checkArgument(0 <= from);
387     Assertions.checkArgument(to <= input.length);
388     return Arrays.copyOfRange(input, from, to);
389   }
390 
391   /**
392    * Creates a new array containing {@code original} with {@code newElement} appended.
393    *
394    * @param original The input array.
395    * @param newElement The element to append.
396    * @return The new array.
397    */
nullSafeArrayAppend(T[] original, T newElement)398   public static <T> T[] nullSafeArrayAppend(T[] original, T newElement) {
399     @NullableType T[] result = Arrays.copyOf(original, original.length + 1);
400     result[original.length] = newElement;
401     return castNonNullTypeArray(result);
402   }
403 
404   /**
405    * Creates a new array containing the concatenation of two non-null type arrays.
406    *
407    * @param first The first array.
408    * @param second The second array.
409    * @return The concatenated result.
410    */
411   @SuppressWarnings("nullness:assignment")
nullSafeArrayConcatenation(T[] first, T[] second)412   public static <T> T[] nullSafeArrayConcatenation(T[] first, T[] second) {
413     T[] concatenation = Arrays.copyOf(first, first.length + second.length);
414     System.arraycopy(
415         /* src= */ second,
416         /* srcPos= */ 0,
417         /* dest= */ concatenation,
418         /* destPos= */ first.length,
419         /* length= */ second.length);
420     return concatenation;
421   }
422 
423   /**
424    * Copies the contents of {@code list} into {@code array}.
425    *
426    * <p>{@code list.size()} must be the same as {@code array.length} to ensure the contents can be
427    * copied into {@code array} without leaving any nulls at the end.
428    *
429    * @param list The list to copy items from.
430    * @param array The array to copy items to.
431    */
432   @SuppressWarnings("nullness:toArray.nullable.elements.not.newarray")
nullSafeListToArray(List<T> list, T[] array)433   public static <T> void nullSafeListToArray(List<T> list, T[] array) {
434     Assertions.checkState(list.size() == array.length);
435     list.toArray(array);
436   }
437 
438   /**
439    * Creates a {@link Handler} on the current {@link Looper} thread.
440    *
441    * @throws IllegalStateException If the current thread doesn't have a {@link Looper}.
442    */
createHandlerForCurrentLooper()443   public static Handler createHandlerForCurrentLooper() {
444     return createHandlerForCurrentLooper(/* callback= */ null);
445   }
446 
447   /**
448    * Creates a {@link Handler} with the specified {@link Handler.Callback} on the current {@link
449    * Looper} thread.
450    *
451    * <p>The method accepts partially initialized objects as callback under the assumption that the
452    * Handler won't be used to send messages until the callback is fully initialized.
453    *
454    * @param callback A {@link Handler.Callback}. May be a partially initialized class, or null if no
455    *     callback is required.
456    * @return A {@link Handler} with the specified callback on the current {@link Looper} thread.
457    * @throws IllegalStateException If the current thread doesn't have a {@link Looper}.
458    */
createHandlerForCurrentLooper( @ullable Handler.@nknownInitialization Callback callback)459   public static Handler createHandlerForCurrentLooper(
460       @Nullable Handler.@UnknownInitialization Callback callback) {
461     return createHandler(Assertions.checkStateNotNull(Looper.myLooper()), callback);
462   }
463 
464   /**
465    * Creates a {@link Handler} on the current {@link Looper} thread.
466    *
467    * <p>If the current thread doesn't have a {@link Looper}, the application's main thread {@link
468    * Looper} is used.
469    */
createHandlerForCurrentOrMainLooper()470   public static Handler createHandlerForCurrentOrMainLooper() {
471     return createHandlerForCurrentOrMainLooper(/* callback= */ null);
472   }
473 
474   /**
475    * Creates a {@link Handler} with the specified {@link Handler.Callback} on the current {@link
476    * Looper} thread.
477    *
478    * <p>The method accepts partially initialized objects as callback under the assumption that the
479    * Handler won't be used to send messages until the callback is fully initialized.
480    *
481    * <p>If the current thread doesn't have a {@link Looper}, the application's main thread {@link
482    * Looper} is used.
483    *
484    * @param callback A {@link Handler.Callback}. May be a partially initialized class, or null if no
485    *     callback is required.
486    * @return A {@link Handler} with the specified callback on the current {@link Looper} thread.
487    */
createHandlerForCurrentOrMainLooper( @ullable Handler.@nknownInitialization Callback callback)488   public static Handler createHandlerForCurrentOrMainLooper(
489       @Nullable Handler.@UnknownInitialization Callback callback) {
490     return createHandler(getCurrentOrMainLooper(), callback);
491   }
492 
493   /**
494    * Creates a {@link Handler} with the specified {@link Handler.Callback} on the specified {@link
495    * Looper} thread.
496    *
497    * <p>The method accepts partially initialized objects as callback under the assumption that the
498    * Handler won't be used to send messages until the callback is fully initialized.
499    *
500    * @param looper A {@link Looper} to run the callback on.
501    * @param callback A {@link Handler.Callback}. May be a partially initialized class, or null if no
502    *     callback is required.
503    * @return A {@link Handler} with the specified callback on the current {@link Looper} thread.
504    */
505   @SuppressWarnings({"nullness:argument", "nullness:return"})
createHandler( Looper looper, @Nullable Handler.@UnknownInitialization Callback callback)506   public static Handler createHandler(
507       Looper looper, @Nullable Handler.@UnknownInitialization Callback callback) {
508     return new Handler(looper, callback);
509   }
510 
511   /**
512    * Posts the {@link Runnable} if the calling thread differs with the {@link Looper} of the {@link
513    * Handler}. Otherwise, runs the {@link Runnable} directly.
514    *
515    * @param handler The handler to which the {@link Runnable} will be posted.
516    * @param runnable The runnable to either post or run.
517    * @return {@code true} if the {@link Runnable} was successfully posted to the {@link Handler} or
518    *     run. {@code false} otherwise.
519    */
postOrRun(Handler handler, Runnable runnable)520   public static boolean postOrRun(Handler handler, Runnable runnable) {
521     Looper looper = handler.getLooper();
522     if (!looper.getThread().isAlive()) {
523       return false;
524     }
525     if (handler.getLooper() == Looper.myLooper()) {
526       runnable.run();
527       return true;
528     } else {
529       return handler.post(runnable);
530     }
531   }
532 
533   /**
534    * Returns the {@link Looper} associated with the current thread, or the {@link Looper} of the
535    * application's main thread if the current thread doesn't have a {@link Looper}.
536    */
getCurrentOrMainLooper()537   public static Looper getCurrentOrMainLooper() {
538     @Nullable Looper myLooper = Looper.myLooper();
539     return myLooper != null ? myLooper : Looper.getMainLooper();
540   }
541 
542   /**
543    * Instantiates a new single threaded executor whose thread has the specified name.
544    *
545    * @param threadName The name of the thread.
546    * @return The executor.
547    */
newSingleThreadExecutor(String threadName)548   public static ExecutorService newSingleThreadExecutor(String threadName) {
549     return Executors.newSingleThreadExecutor(runnable -> new Thread(runnable, threadName));
550   }
551 
552   /**
553    * Closes a {@link Closeable}, suppressing any {@link IOException} that may occur. Both {@link
554    * java.io.OutputStream} and {@link InputStream} are {@code Closeable}.
555    *
556    * @param closeable The {@link Closeable} to close.
557    */
closeQuietly(@ullable Closeable closeable)558   public static void closeQuietly(@Nullable Closeable closeable) {
559     try {
560       if (closeable != null) {
561         closeable.close();
562       }
563     } catch (IOException e) {
564       // Ignore.
565     }
566   }
567 
568   /**
569    * Reads an integer from a {@link Parcel} and interprets it as a boolean, with 0 mapping to false
570    * and all other values mapping to true.
571    *
572    * @param parcel The {@link Parcel} to read from.
573    * @return The read value.
574    */
readBoolean(Parcel parcel)575   public static boolean readBoolean(Parcel parcel) {
576     return parcel.readInt() != 0;
577   }
578 
579   /**
580    * Writes a boolean to a {@link Parcel}. The boolean is written as an integer with value 1 (true)
581    * or 0 (false).
582    *
583    * @param parcel The {@link Parcel} to write to.
584    * @param value The value to write.
585    */
writeBoolean(Parcel parcel, boolean value)586   public static void writeBoolean(Parcel parcel, boolean value) {
587     parcel.writeInt(value ? 1 : 0);
588   }
589 
590   /**
591    * Returns the language tag for a {@link Locale}.
592    *
593    * <p>For API levels &ge; 21, this tag is IETF BCP 47 compliant. Use {@link
594    * #normalizeLanguageCode(String)} to retrieve a normalized IETF BCP 47 language tag for all API
595    * levels if needed.
596    *
597    * @param locale A {@link Locale}.
598    * @return The language tag.
599    */
getLocaleLanguageTag(Locale locale)600   public static String getLocaleLanguageTag(Locale locale) {
601     return SDK_INT >= 21 ? getLocaleLanguageTagV21(locale) : locale.toString();
602   }
603 
604   /**
605    * Returns a normalized IETF BCP 47 language tag for {@code language}.
606    *
607    * @param language A case-insensitive language code supported by {@link
608    *     Locale#forLanguageTag(String)}.
609    * @return The all-lowercase normalized code, or null if the input was null, or {@code
610    *     language.toLowerCase()} if the language could not be normalized.
611    */
normalizeLanguageCode(@olyNull String language)612   public static @PolyNull String normalizeLanguageCode(@PolyNull String language) {
613     if (language == null) {
614       return null;
615     }
616     // Locale data (especially for API < 21) may produce tags with '_' instead of the
617     // standard-conformant '-'.
618     String normalizedTag = language.replace('_', '-');
619     if (normalizedTag.isEmpty() || normalizedTag.equals(C.LANGUAGE_UNDETERMINED)) {
620       // Tag isn't valid, keep using the original.
621       normalizedTag = language;
622     }
623     normalizedTag = Ascii.toLowerCase(normalizedTag);
624     String mainLanguage = Util.splitAtFirst(normalizedTag, "-")[0];
625     if (languageTagReplacementMap == null) {
626       languageTagReplacementMap = createIsoLanguageReplacementMap();
627     }
628     @Nullable String replacedLanguage = languageTagReplacementMap.get(mainLanguage);
629     if (replacedLanguage != null) {
630       normalizedTag =
631           replacedLanguage + normalizedTag.substring(/* beginIndex= */ mainLanguage.length());
632       mainLanguage = replacedLanguage;
633     }
634     if ("no".equals(mainLanguage) || "i".equals(mainLanguage) || "zh".equals(mainLanguage)) {
635       normalizedTag = maybeReplaceLegacyLanguageTags(normalizedTag);
636     }
637     return normalizedTag;
638   }
639 
640   /**
641    * Returns a new {@link String} constructed by decoding UTF-8 encoded bytes.
642    *
643    * @param bytes The UTF-8 encoded bytes to decode.
644    * @return The string.
645    */
fromUtf8Bytes(byte[] bytes)646   public static String fromUtf8Bytes(byte[] bytes) {
647     return new String(bytes, Charsets.UTF_8);
648   }
649 
650   /**
651    * Returns a new {@link String} constructed by decoding UTF-8 encoded bytes in a subarray.
652    *
653    * @param bytes The UTF-8 encoded bytes to decode.
654    * @param offset The index of the first byte to decode.
655    * @param length The number of bytes to decode.
656    * @return The string.
657    */
fromUtf8Bytes(byte[] bytes, int offset, int length)658   public static String fromUtf8Bytes(byte[] bytes, int offset, int length) {
659     return new String(bytes, offset, length, Charsets.UTF_8);
660   }
661 
662   /**
663    * Returns a new byte array containing the code points of a {@link String} encoded using UTF-8.
664    *
665    * @param value The {@link String} whose bytes should be obtained.
666    * @return The code points encoding using UTF-8.
667    */
getUtf8Bytes(String value)668   public static byte[] getUtf8Bytes(String value) {
669     return value.getBytes(Charsets.UTF_8);
670   }
671 
672   /**
673    * Splits a string using {@code value.split(regex, -1}). Note: this is is similar to {@link
674    * String#split(String)} but empty matches at the end of the string will not be omitted from the
675    * returned array.
676    *
677    * @param value The string to split.
678    * @param regex A delimiting regular expression.
679    * @return The array of strings resulting from splitting the string.
680    */
split(String value, String regex)681   public static String[] split(String value, String regex) {
682     return value.split(regex, /* limit= */ -1);
683   }
684 
685   /**
686    * Splits the string at the first occurrence of the delimiter {@code regex}. If the delimiter does
687    * not match, returns an array with one element which is the input string. If the delimiter does
688    * match, returns an array with the portion of the string before the delimiter and the rest of the
689    * string.
690    *
691    * @param value The string.
692    * @param regex A delimiting regular expression.
693    * @return The string split by the first occurrence of the delimiter.
694    */
splitAtFirst(String value, String regex)695   public static String[] splitAtFirst(String value, String regex) {
696     return value.split(regex, /* limit= */ 2);
697   }
698 
699   /**
700    * Returns whether the given character is a carriage return ('\r') or a line feed ('\n').
701    *
702    * @param c The character.
703    * @return Whether the given character is a linebreak.
704    */
isLinebreak(int c)705   public static boolean isLinebreak(int c) {
706     return c == '\n' || c == '\r';
707   }
708 
709   /**
710    * Formats a string using {@link Locale#US}.
711    *
712    * @see String#format(String, Object...)
713    */
formatInvariant(String format, Object... args)714   public static String formatInvariant(String format, Object... args) {
715     return String.format(Locale.US, format, args);
716   }
717 
718   /**
719    * Divides a {@code numerator} by a {@code denominator}, returning the ceiled result.
720    *
721    * @param numerator The numerator to divide.
722    * @param denominator The denominator to divide by.
723    * @return The ceiled result of the division.
724    */
ceilDivide(int numerator, int denominator)725   public static int ceilDivide(int numerator, int denominator) {
726     return (numerator + denominator - 1) / denominator;
727   }
728 
729   /**
730    * Divides a {@code numerator} by a {@code denominator}, returning the ceiled result.
731    *
732    * @param numerator The numerator to divide.
733    * @param denominator The denominator to divide by.
734    * @return The ceiled result of the division.
735    */
ceilDivide(long numerator, long denominator)736   public static long ceilDivide(long numerator, long denominator) {
737     return (numerator + denominator - 1) / denominator;
738   }
739 
740   /**
741    * Constrains a value to the specified bounds.
742    *
743    * @param value The value to constrain.
744    * @param min The lower bound.
745    * @param max The upper bound.
746    * @return The constrained value {@code Math.max(min, Math.min(value, max))}.
747    */
constrainValue(int value, int min, int max)748   public static int constrainValue(int value, int min, int max) {
749     return max(min, min(value, max));
750   }
751 
752   /**
753    * Constrains a value to the specified bounds.
754    *
755    * @param value The value to constrain.
756    * @param min The lower bound.
757    * @param max The upper bound.
758    * @return The constrained value {@code Math.max(min, Math.min(value, max))}.
759    */
constrainValue(long value, long min, long max)760   public static long constrainValue(long value, long min, long max) {
761     return max(min, min(value, max));
762   }
763 
764   /**
765    * Constrains a value to the specified bounds.
766    *
767    * @param value The value to constrain.
768    * @param min The lower bound.
769    * @param max The upper bound.
770    * @return The constrained value {@code Math.max(min, Math.min(value, max))}.
771    */
constrainValue(float value, float min, float max)772   public static float constrainValue(float value, float min, float max) {
773     return max(min, min(value, max));
774   }
775 
776   /**
777    * Returns the sum of two arguments, or a third argument if the result overflows.
778    *
779    * @param x The first value.
780    * @param y The second value.
781    * @param overflowResult The return value if {@code x + y} overflows.
782    * @return {@code x + y}, or {@code overflowResult} if the result overflows.
783    */
addWithOverflowDefault(long x, long y, long overflowResult)784   public static long addWithOverflowDefault(long x, long y, long overflowResult) {
785     long result = x + y;
786     // See Hacker's Delight 2-13 (H. Warren Jr).
787     if (((x ^ result) & (y ^ result)) < 0) {
788       return overflowResult;
789     }
790     return result;
791   }
792 
793   /**
794    * Returns the difference between two arguments, or a third argument if the result overflows.
795    *
796    * @param x The first value.
797    * @param y The second value.
798    * @param overflowResult The return value if {@code x - y} overflows.
799    * @return {@code x - y}, or {@code overflowResult} if the result overflows.
800    */
subtractWithOverflowDefault(long x, long y, long overflowResult)801   public static long subtractWithOverflowDefault(long x, long y, long overflowResult) {
802     long result = x - y;
803     // See Hacker's Delight 2-13 (H. Warren Jr).
804     if (((x ^ y) & (x ^ result)) < 0) {
805       return overflowResult;
806     }
807     return result;
808   }
809 
810   /**
811    * Returns the index of the first occurrence of {@code value} in {@code array}, or {@link
812    * C#INDEX_UNSET} if {@code value} is not contained in {@code array}.
813    *
814    * @param array The array to search.
815    * @param value The value to search for.
816    * @return The index of the first occurrence of value in {@code array}, or {@link C#INDEX_UNSET}
817    *     if {@code value} is not contained in {@code array}.
818    */
linearSearch(int[] array, int value)819   public static int linearSearch(int[] array, int value) {
820     for (int i = 0; i < array.length; i++) {
821       if (array[i] == value) {
822         return i;
823       }
824     }
825     return C.INDEX_UNSET;
826   }
827 
828   /**
829    * Returns the index of the first occurrence of {@code value} in {@code array}, or {@link
830    * C#INDEX_UNSET} if {@code value} is not contained in {@code array}.
831    *
832    * @param array The array to search.
833    * @param value The value to search for.
834    * @return The index of the first occurrence of value in {@code array}, or {@link C#INDEX_UNSET}
835    *     if {@code value} is not contained in {@code array}.
836    */
linearSearch(long[] array, long value)837   public static int linearSearch(long[] array, long value) {
838     for (int i = 0; i < array.length; i++) {
839       if (array[i] == value) {
840         return i;
841       }
842     }
843     return C.INDEX_UNSET;
844   }
845 
846   /**
847    * Returns the index of the largest element in {@code array} that is less than (or optionally
848    * equal to) a specified {@code value}.
849    *
850    * <p>The search is performed using a binary search algorithm, so the array must be sorted. If the
851    * array contains multiple elements equal to {@code value} and {@code inclusive} is true, the
852    * index of the first one will be returned.
853    *
854    * @param array The array to search.
855    * @param value The value being searched for.
856    * @param inclusive If the value is present in the array, whether to return the corresponding
857    *     index. If false then the returned index corresponds to the largest element strictly less
858    *     than the value.
859    * @param stayInBounds If true, then 0 will be returned in the case that the value is smaller than
860    *     the smallest element in the array. If false then -1 will be returned.
861    * @return The index of the largest element in {@code array} that is less than (or optionally
862    *     equal to) {@code value}.
863    */
binarySearchFloor( int[] array, int value, boolean inclusive, boolean stayInBounds)864   public static int binarySearchFloor(
865       int[] array, int value, boolean inclusive, boolean stayInBounds) {
866     int index = Arrays.binarySearch(array, value);
867     if (index < 0) {
868       index = -(index + 2);
869     } else {
870       while (--index >= 0 && array[index] == value) {}
871       if (inclusive) {
872         index++;
873       }
874     }
875     return stayInBounds ? max(0, index) : index;
876   }
877 
878   /**
879    * Returns the index of the largest element in {@code array} that is less than (or optionally
880    * equal to) a specified {@code value}.
881    *
882    * <p>The search is performed using a binary search algorithm, so the array must be sorted. If the
883    * array contains multiple elements equal to {@code value} and {@code inclusive} is true, the
884    * index of the first one will be returned.
885    *
886    * @param array The array to search.
887    * @param value The value being searched for.
888    * @param inclusive If the value is present in the array, whether to return the corresponding
889    *     index. If false then the returned index corresponds to the largest element strictly less
890    *     than the value.
891    * @param stayInBounds If true, then 0 will be returned in the case that the value is smaller than
892    *     the smallest element in the array. If false then -1 will be returned.
893    * @return The index of the largest element in {@code array} that is less than (or optionally
894    *     equal to) {@code value}.
895    */
binarySearchFloor( long[] array, long value, boolean inclusive, boolean stayInBounds)896   public static int binarySearchFloor(
897       long[] array, long value, boolean inclusive, boolean stayInBounds) {
898     int index = Arrays.binarySearch(array, value);
899     if (index < 0) {
900       index = -(index + 2);
901     } else {
902       while (--index >= 0 && array[index] == value) {}
903       if (inclusive) {
904         index++;
905       }
906     }
907     return stayInBounds ? max(0, index) : index;
908   }
909 
910   /**
911    * Returns the index of the largest element in {@code list} that is less than (or optionally equal
912    * to) a specified {@code value}.
913    *
914    * <p>The search is performed using a binary search algorithm, so the list must be sorted. If the
915    * list contains multiple elements equal to {@code value} and {@code inclusive} is true, the index
916    * of the first one will be returned.
917    *
918    * @param <T> The type of values being searched.
919    * @param list The list to search.
920    * @param value The value being searched for.
921    * @param inclusive If the value is present in the list, whether to return the corresponding
922    *     index. If false then the returned index corresponds to the largest element strictly less
923    *     than the value.
924    * @param stayInBounds If true, then 0 will be returned in the case that the value is smaller than
925    *     the smallest element in the list. If false then -1 will be returned.
926    * @return The index of the largest element in {@code list} that is less than (or optionally equal
927    *     to) {@code value}.
928    */
binarySearchFloor( List<? extends Comparable<? super T>> list, T value, boolean inclusive, boolean stayInBounds)929   public static <T extends Comparable<? super T>> int binarySearchFloor(
930       List<? extends Comparable<? super T>> list,
931       T value,
932       boolean inclusive,
933       boolean stayInBounds) {
934     int index = Collections.binarySearch(list, value);
935     if (index < 0) {
936       index = -(index + 2);
937     } else {
938       while (--index >= 0 && list.get(index).compareTo(value) == 0) {}
939       if (inclusive) {
940         index++;
941       }
942     }
943     return stayInBounds ? max(0, index) : index;
944   }
945 
946   /**
947    * Returns the index of the largest element in {@code longArray} that is less than (or optionally
948    * equal to) a specified {@code value}.
949    *
950    * <p>The search is performed using a binary search algorithm, so the array must be sorted. If the
951    * array contains multiple elements equal to {@code value} and {@code inclusive} is true, the
952    * index of the first one will be returned.
953    *
954    * @param longArray The array to search.
955    * @param value The value being searched for.
956    * @param inclusive If the value is present in the array, whether to return the corresponding
957    *     index. If false then the returned index corresponds to the largest element strictly less
958    *     than the value.
959    * @param stayInBounds If true, then 0 will be returned in the case that the value is smaller than
960    *     the smallest element in the array. If false then -1 will be returned.
961    * @return The index of the largest element in {@code array} that is less than (or optionally
962    *     equal to) {@code value}.
963    */
binarySearchFloor( LongArray longArray, long value, boolean inclusive, boolean stayInBounds)964   public static int binarySearchFloor(
965       LongArray longArray, long value, boolean inclusive, boolean stayInBounds) {
966     int lowIndex = 0;
967     int highIndex = longArray.size() - 1;
968 
969     while (lowIndex <= highIndex) {
970       int midIndex = (lowIndex + highIndex) >>> 1;
971       if (longArray.get(midIndex) < value) {
972         lowIndex = midIndex + 1;
973       } else {
974         highIndex = midIndex - 1;
975       }
976     }
977 
978     if (inclusive && highIndex + 1 < longArray.size() && longArray.get(highIndex + 1) == value) {
979       highIndex++;
980     } else if (stayInBounds && highIndex == -1) {
981       highIndex = 0;
982     }
983 
984     return highIndex;
985   }
986 
987   /**
988    * Returns the index of the smallest element in {@code array} that is greater than (or optionally
989    * equal to) a specified {@code value}.
990    *
991    * <p>The search is performed using a binary search algorithm, so the array must be sorted. If the
992    * array contains multiple elements equal to {@code value} and {@code inclusive} is true, the
993    * index of the last one will be returned.
994    *
995    * @param array The array to search.
996    * @param value The value being searched for.
997    * @param inclusive If the value is present in the array, whether to return the corresponding
998    *     index. If false then the returned index corresponds to the smallest element strictly
999    *     greater than the value.
1000    * @param stayInBounds If true, then {@code (a.length - 1)} will be returned in the case that the
1001    *     value is greater than the largest element in the array. If false then {@code a.length} will
1002    *     be returned.
1003    * @return The index of the smallest element in {@code array} that is greater than (or optionally
1004    *     equal to) {@code value}.
1005    */
binarySearchCeil( int[] array, int value, boolean inclusive, boolean stayInBounds)1006   public static int binarySearchCeil(
1007       int[] array, int value, boolean inclusive, boolean stayInBounds) {
1008     int index = Arrays.binarySearch(array, value);
1009     if (index < 0) {
1010       index = ~index;
1011     } else {
1012       while (++index < array.length && array[index] == value) {}
1013       if (inclusive) {
1014         index--;
1015       }
1016     }
1017     return stayInBounds ? min(array.length - 1, index) : index;
1018   }
1019 
1020   /**
1021    * Returns the index of the smallest element in {@code array} that is greater than (or optionally
1022    * equal to) a specified {@code value}.
1023    *
1024    * <p>The search is performed using a binary search algorithm, so the array must be sorted. If the
1025    * array contains multiple elements equal to {@code value} and {@code inclusive} is true, the
1026    * index of the last one will be returned.
1027    *
1028    * @param array The array to search.
1029    * @param value The value being searched for.
1030    * @param inclusive If the value is present in the array, whether to return the corresponding
1031    *     index. If false then the returned index corresponds to the smallest element strictly
1032    *     greater than the value.
1033    * @param stayInBounds If true, then {@code (a.length - 1)} will be returned in the case that the
1034    *     value is greater than the largest element in the array. If false then {@code a.length} will
1035    *     be returned.
1036    * @return The index of the smallest element in {@code array} that is greater than (or optionally
1037    *     equal to) {@code value}.
1038    */
binarySearchCeil( long[] array, long value, boolean inclusive, boolean stayInBounds)1039   public static int binarySearchCeil(
1040       long[] array, long value, boolean inclusive, boolean stayInBounds) {
1041     int index = Arrays.binarySearch(array, value);
1042     if (index < 0) {
1043       index = ~index;
1044     } else {
1045       while (++index < array.length && array[index] == value) {}
1046       if (inclusive) {
1047         index--;
1048       }
1049     }
1050     return stayInBounds ? min(array.length - 1, index) : index;
1051   }
1052 
1053   /**
1054    * Returns the index of the smallest element in {@code list} that is greater than (or optionally
1055    * equal to) a specified value.
1056    *
1057    * <p>The search is performed using a binary search algorithm, so the list must be sorted. If the
1058    * list contains multiple elements equal to {@code value} and {@code inclusive} is true, the index
1059    * of the last one will be returned.
1060    *
1061    * @param <T> The type of values being searched.
1062    * @param list The list to search.
1063    * @param value The value being searched for.
1064    * @param inclusive If the value is present in the list, whether to return the corresponding
1065    *     index. If false then the returned index corresponds to the smallest element strictly
1066    *     greater than the value.
1067    * @param stayInBounds If true, then {@code (list.size() - 1)} will be returned in the case that
1068    *     the value is greater than the largest element in the list. If false then {@code
1069    *     list.size()} will be returned.
1070    * @return The index of the smallest element in {@code list} that is greater than (or optionally
1071    *     equal to) {@code value}.
1072    */
binarySearchCeil( List<? extends Comparable<? super T>> list, T value, boolean inclusive, boolean stayInBounds)1073   public static <T extends Comparable<? super T>> int binarySearchCeil(
1074       List<? extends Comparable<? super T>> list,
1075       T value,
1076       boolean inclusive,
1077       boolean stayInBounds) {
1078     int index = Collections.binarySearch(list, value);
1079     if (index < 0) {
1080       index = ~index;
1081     } else {
1082       int listSize = list.size();
1083       while (++index < listSize && list.get(index).compareTo(value) == 0) {}
1084       if (inclusive) {
1085         index--;
1086       }
1087     }
1088     return stayInBounds ? min(list.size() - 1, index) : index;
1089   }
1090 
1091   /**
1092    * Compares two long values and returns the same value as {@code Long.compare(long, long)}.
1093    *
1094    * @param left The left operand.
1095    * @param right The right operand.
1096    * @return 0, if left == right, a negative value if left &lt; right, or a positive value if left
1097    *     &gt; right.
1098    */
compareLong(long left, long right)1099   public static int compareLong(long left, long right) {
1100     return left < right ? -1 : left == right ? 0 : 1;
1101   }
1102 
1103   /**
1104    * Returns the minimum value in the given {@link SparseLongArray}.
1105    *
1106    * @param sparseLongArray The {@link SparseLongArray}.
1107    * @return The minimum value.
1108    * @throws NoSuchElementException If the array is empty.
1109    */
1110   @RequiresApi(18)
minValue(SparseLongArray sparseLongArray)1111   public static long minValue(SparseLongArray sparseLongArray) {
1112     if (sparseLongArray.size() == 0) {
1113       throw new NoSuchElementException();
1114     }
1115     long min = Long.MAX_VALUE;
1116     for (int i = 0; i < sparseLongArray.size(); i++) {
1117       min = min(min, sparseLongArray.valueAt(i));
1118     }
1119     return min;
1120   }
1121 
1122   /**
1123    * Returns the maximum value in the given {@link SparseLongArray}.
1124    *
1125    * @param sparseLongArray The {@link SparseLongArray}.
1126    * @return The maximum value.
1127    * @throws NoSuchElementException If the array is empty.
1128    */
1129   @RequiresApi(18)
maxValue(SparseLongArray sparseLongArray)1130   public static long maxValue(SparseLongArray sparseLongArray) {
1131     if (sparseLongArray.size() == 0) {
1132       throw new NoSuchElementException();
1133     }
1134     long max = Long.MIN_VALUE;
1135     for (int i = 0; i < sparseLongArray.size(); i++) {
1136       max = max(max, sparseLongArray.valueAt(i));
1137     }
1138     return max;
1139   }
1140 
1141   /**
1142    * Converts a time in microseconds to the corresponding time in milliseconds, preserving {@link
1143    * C#TIME_UNSET} and {@link C#TIME_END_OF_SOURCE} values.
1144    *
1145    * @param timeUs The time in microseconds.
1146    * @return The corresponding time in milliseconds.
1147    */
usToMs(long timeUs)1148   public static long usToMs(long timeUs) {
1149     return (timeUs == C.TIME_UNSET || timeUs == C.TIME_END_OF_SOURCE) ? timeUs : (timeUs / 1000);
1150   }
1151 
1152   /**
1153    * Converts a time in milliseconds to the corresponding time in microseconds, preserving {@link
1154    * C#TIME_UNSET} values and {@link C#TIME_END_OF_SOURCE} values.
1155    *
1156    * @param timeMs The time in milliseconds.
1157    * @return The corresponding time in microseconds.
1158    */
msToUs(long timeMs)1159   public static long msToUs(long timeMs) {
1160     return (timeMs == C.TIME_UNSET || timeMs == C.TIME_END_OF_SOURCE) ? timeMs : (timeMs * 1000);
1161   }
1162 
1163   /**
1164    * Parses an xs:duration attribute value, returning the parsed duration in milliseconds.
1165    *
1166    * @param value The attribute value to decode.
1167    * @return The parsed duration in milliseconds.
1168    */
parseXsDuration(String value)1169   public static long parseXsDuration(String value) {
1170     Matcher matcher = XS_DURATION_PATTERN.matcher(value);
1171     if (matcher.matches()) {
1172       boolean negated = !TextUtils.isEmpty(matcher.group(1));
1173       // Durations containing years and months aren't completely defined. We assume there are
1174       // 30.4368 days in a month, and 365.242 days in a year.
1175       String years = matcher.group(3);
1176       double durationSeconds = (years != null) ? Double.parseDouble(years) * 31556908 : 0;
1177       String months = matcher.group(5);
1178       durationSeconds += (months != null) ? Double.parseDouble(months) * 2629739 : 0;
1179       String days = matcher.group(7);
1180       durationSeconds += (days != null) ? Double.parseDouble(days) * 86400 : 0;
1181       String hours = matcher.group(10);
1182       durationSeconds += (hours != null) ? Double.parseDouble(hours) * 3600 : 0;
1183       String minutes = matcher.group(12);
1184       durationSeconds += (minutes != null) ? Double.parseDouble(minutes) * 60 : 0;
1185       String seconds = matcher.group(14);
1186       durationSeconds += (seconds != null) ? Double.parseDouble(seconds) : 0;
1187       long durationMillis = (long) (durationSeconds * 1000);
1188       return negated ? -durationMillis : durationMillis;
1189     } else {
1190       return (long) (Double.parseDouble(value) * 3600 * 1000);
1191     }
1192   }
1193 
1194   /**
1195    * Parses an xs:dateTime attribute value, returning the parsed timestamp in milliseconds since the
1196    * epoch.
1197    *
1198    * @param value The attribute value to decode.
1199    * @return The parsed timestamp in milliseconds since the epoch.
1200    * @throws ParserException if an error occurs parsing the dateTime attribute value.
1201    */
1202   // incompatible types in argument.
1203   // dereference of possibly-null reference matcher.group(9)
1204   @SuppressWarnings({"nullness:argument", "nullness:dereference.of.nullable"})
parseXsDateTime(String value)1205   public static long parseXsDateTime(String value) throws ParserException {
1206     Matcher matcher = XS_DATE_TIME_PATTERN.matcher(value);
1207     if (!matcher.matches()) {
1208       throw ParserException.createForMalformedContainer(
1209           "Invalid date/time format: " + value, /* cause= */ null);
1210     }
1211 
1212     int timezoneShift;
1213     if (matcher.group(9) == null) {
1214       // No time zone specified.
1215       timezoneShift = 0;
1216     } else if (matcher.group(9).equalsIgnoreCase("Z")) {
1217       timezoneShift = 0;
1218     } else {
1219       timezoneShift =
1220           ((Integer.parseInt(matcher.group(12)) * 60 + Integer.parseInt(matcher.group(13))));
1221       if ("-".equals(matcher.group(11))) {
1222         timezoneShift *= -1;
1223       }
1224     }
1225 
1226     Calendar dateTime = new GregorianCalendar(TimeZone.getTimeZone("GMT"));
1227 
1228     dateTime.clear();
1229     // Note: The month value is 0-based, hence the -1 on group(2)
1230     dateTime.set(
1231         Integer.parseInt(matcher.group(1)),
1232         Integer.parseInt(matcher.group(2)) - 1,
1233         Integer.parseInt(matcher.group(3)),
1234         Integer.parseInt(matcher.group(4)),
1235         Integer.parseInt(matcher.group(5)),
1236         Integer.parseInt(matcher.group(6)));
1237     if (!TextUtils.isEmpty(matcher.group(8))) {
1238       final BigDecimal bd = new BigDecimal("0." + matcher.group(8));
1239       // we care only for milliseconds, so movePointRight(3)
1240       dateTime.set(Calendar.MILLISECOND, bd.movePointRight(3).intValue());
1241     }
1242 
1243     long time = dateTime.getTimeInMillis();
1244     if (timezoneShift != 0) {
1245       time -= timezoneShift * 60000;
1246     }
1247 
1248     return time;
1249   }
1250 
1251   /**
1252    * Scales a large timestamp.
1253    *
1254    * <p>Logically, scaling consists of a multiplication followed by a division. The actual
1255    * operations performed are designed to minimize the probability of overflow.
1256    *
1257    * @param timestamp The timestamp to scale.
1258    * @param multiplier The multiplier.
1259    * @param divisor The divisor.
1260    * @return The scaled timestamp.
1261    */
scaleLargeTimestamp(long timestamp, long multiplier, long divisor)1262   public static long scaleLargeTimestamp(long timestamp, long multiplier, long divisor) {
1263     if (divisor >= multiplier && (divisor % multiplier) == 0) {
1264       long divisionFactor = divisor / multiplier;
1265       return timestamp / divisionFactor;
1266     } else if (divisor < multiplier && (multiplier % divisor) == 0) {
1267       long multiplicationFactor = multiplier / divisor;
1268       return timestamp * multiplicationFactor;
1269     } else {
1270       double multiplicationFactor = (double) multiplier / divisor;
1271       return (long) (timestamp * multiplicationFactor);
1272     }
1273   }
1274 
1275   /**
1276    * Applies {@link #scaleLargeTimestamp(long, long, long)} to a list of unscaled timestamps.
1277    *
1278    * @param timestamps The timestamps to scale.
1279    * @param multiplier The multiplier.
1280    * @param divisor The divisor.
1281    * @return The scaled timestamps.
1282    */
scaleLargeTimestamps(List<Long> timestamps, long multiplier, long divisor)1283   public static long[] scaleLargeTimestamps(List<Long> timestamps, long multiplier, long divisor) {
1284     long[] scaledTimestamps = new long[timestamps.size()];
1285     if (divisor >= multiplier && (divisor % multiplier) == 0) {
1286       long divisionFactor = divisor / multiplier;
1287       for (int i = 0; i < scaledTimestamps.length; i++) {
1288         scaledTimestamps[i] = timestamps.get(i) / divisionFactor;
1289       }
1290     } else if (divisor < multiplier && (multiplier % divisor) == 0) {
1291       long multiplicationFactor = multiplier / divisor;
1292       for (int i = 0; i < scaledTimestamps.length; i++) {
1293         scaledTimestamps[i] = timestamps.get(i) * multiplicationFactor;
1294       }
1295     } else {
1296       double multiplicationFactor = (double) multiplier / divisor;
1297       for (int i = 0; i < scaledTimestamps.length; i++) {
1298         scaledTimestamps[i] = (long) (timestamps.get(i) * multiplicationFactor);
1299       }
1300     }
1301     return scaledTimestamps;
1302   }
1303 
1304   /**
1305    * Applies {@link #scaleLargeTimestamp(long, long, long)} to an array of unscaled timestamps.
1306    *
1307    * @param timestamps The timestamps to scale.
1308    * @param multiplier The multiplier.
1309    * @param divisor The divisor.
1310    */
scaleLargeTimestampsInPlace(long[] timestamps, long multiplier, long divisor)1311   public static void scaleLargeTimestampsInPlace(long[] timestamps, long multiplier, long divisor) {
1312     if (divisor >= multiplier && (divisor % multiplier) == 0) {
1313       long divisionFactor = divisor / multiplier;
1314       for (int i = 0; i < timestamps.length; i++) {
1315         timestamps[i] /= divisionFactor;
1316       }
1317     } else if (divisor < multiplier && (multiplier % divisor) == 0) {
1318       long multiplicationFactor = multiplier / divisor;
1319       for (int i = 0; i < timestamps.length; i++) {
1320         timestamps[i] *= multiplicationFactor;
1321       }
1322     } else {
1323       double multiplicationFactor = (double) multiplier / divisor;
1324       for (int i = 0; i < timestamps.length; i++) {
1325         timestamps[i] = (long) (timestamps[i] * multiplicationFactor);
1326       }
1327     }
1328   }
1329 
1330   /**
1331    * Returns the duration of media that will elapse in {@code playoutDuration}.
1332    *
1333    * @param playoutDuration The duration to scale.
1334    * @param speed The factor by which playback is sped up.
1335    * @return The scaled duration, in the same units as {@code playoutDuration}.
1336    */
getMediaDurationForPlayoutDuration(long playoutDuration, float speed)1337   public static long getMediaDurationForPlayoutDuration(long playoutDuration, float speed) {
1338     if (speed == 1f) {
1339       return playoutDuration;
1340     }
1341     return Math.round((double) playoutDuration * speed);
1342   }
1343 
1344   /**
1345    * Returns the playout duration of {@code mediaDuration} of media.
1346    *
1347    * @param mediaDuration The duration to scale.
1348    * @return The scaled duration, in the same units as {@code mediaDuration}.
1349    */
getPlayoutDurationForMediaDuration(long mediaDuration, float speed)1350   public static long getPlayoutDurationForMediaDuration(long mediaDuration, float speed) {
1351     if (speed == 1f) {
1352       return mediaDuration;
1353     }
1354     return Math.round((double) mediaDuration / speed);
1355   }
1356 
1357   /**
1358    * Returns the integer equal to the big-endian concatenation of the characters in {@code string}
1359    * as bytes. The string must be no more than four characters long.
1360    *
1361    * @param string A string no more than four characters long.
1362    */
getIntegerCodeForString(String string)1363   public static int getIntegerCodeForString(String string) {
1364     int length = string.length();
1365     Assertions.checkArgument(length <= 4);
1366     int result = 0;
1367     for (int i = 0; i < length; i++) {
1368       result <<= 8;
1369       result |= string.charAt(i);
1370     }
1371     return result;
1372   }
1373 
1374   /**
1375    * Converts an integer to a long by unsigned conversion.
1376    *
1377    * <p>This method is equivalent to {@link Integer#toUnsignedLong(int)} for API 26+.
1378    */
toUnsignedLong(int x)1379   public static long toUnsignedLong(int x) {
1380     // x is implicitly casted to a long before the bit operation is executed but this does not
1381     // impact the method correctness.
1382     return x & 0xFFFFFFFFL;
1383   }
1384 
1385   /**
1386    * Returns the long that is composed of the bits of the 2 specified integers.
1387    *
1388    * @param mostSignificantBits The 32 most significant bits of the long to return.
1389    * @param leastSignificantBits The 32 least significant bits of the long to return.
1390    * @return a long where its 32 most significant bits are {@code mostSignificantBits} bits and its
1391    *     32 least significant bits are {@code leastSignificantBits}.
1392    */
toLong(int mostSignificantBits, int leastSignificantBits)1393   public static long toLong(int mostSignificantBits, int leastSignificantBits) {
1394     return (toUnsignedLong(mostSignificantBits) << 32) | toUnsignedLong(leastSignificantBits);
1395   }
1396 
1397   /**
1398    * Truncates a sequence of ASCII characters to a maximum length.
1399    *
1400    * <p>This preserves span styling in the {@link CharSequence}. If that's not important, use {@link
1401    * Ascii#truncate(CharSequence, int, String)}.
1402    *
1403    * <p><b>Note:</b> This is not safe to use in general on Unicode text because it may separate
1404    * characters from combining characters or split up surrogate pairs.
1405    *
1406    * @param sequence The character sequence to truncate.
1407    * @param maxLength The max length to truncate to.
1408    * @return {@code sequence} directly if {@code sequence.length() <= maxLength}, otherwise {@code
1409    *     sequence.subsequence(0, maxLength}.
1410    */
truncateAscii(CharSequence sequence, int maxLength)1411   public static CharSequence truncateAscii(CharSequence sequence, int maxLength) {
1412     return sequence.length() <= maxLength ? sequence : sequence.subSequence(0, maxLength);
1413   }
1414 
1415   /**
1416    * Returns a byte array containing values parsed from the hex string provided.
1417    *
1418    * @param hexString The hex string to convert to bytes.
1419    * @return A byte array containing values parsed from the hex string provided.
1420    */
getBytesFromHexString(String hexString)1421   public static byte[] getBytesFromHexString(String hexString) {
1422     byte[] data = new byte[hexString.length() / 2];
1423     for (int i = 0; i < data.length; i++) {
1424       int stringOffset = i * 2;
1425       data[i] =
1426           (byte)
1427               ((Character.digit(hexString.charAt(stringOffset), 16) << 4)
1428                   + Character.digit(hexString.charAt(stringOffset + 1), 16));
1429     }
1430     return data;
1431   }
1432 
1433   /**
1434    * Returns a string containing a lower-case hex representation of the bytes provided.
1435    *
1436    * @param bytes The byte data to convert to hex.
1437    * @return A String containing the hex representation of {@code bytes}.
1438    */
toHexString(byte[] bytes)1439   public static String toHexString(byte[] bytes) {
1440     StringBuilder result = new StringBuilder(bytes.length * 2);
1441     for (int i = 0; i < bytes.length; i++) {
1442       result
1443           .append(Character.forDigit((bytes[i] >> 4) & 0xF, 16))
1444           .append(Character.forDigit(bytes[i] & 0xF, 16));
1445     }
1446     return result.toString();
1447   }
1448 
1449   /**
1450    * Returns a string with comma delimited simple names of each object's class.
1451    *
1452    * @param objects The objects whose simple class names should be comma delimited and returned.
1453    * @return A string with comma delimited simple names of each object's class.
1454    */
getCommaDelimitedSimpleClassNames(Object[] objects)1455   public static String getCommaDelimitedSimpleClassNames(Object[] objects) {
1456     StringBuilder stringBuilder = new StringBuilder();
1457     for (int i = 0; i < objects.length; i++) {
1458       stringBuilder.append(objects[i].getClass().getSimpleName());
1459       if (i < objects.length - 1) {
1460         stringBuilder.append(", ");
1461       }
1462     }
1463     return stringBuilder.toString();
1464   }
1465 
1466   /**
1467    * Returns a user agent string based on the given application name and the library version.
1468    *
1469    * @param context A valid context of the calling application.
1470    * @param applicationName String that will be prefix'ed to the generated user agent.
1471    * @return A user agent string generated using the applicationName and the library version.
1472    */
getUserAgent(Context context, String applicationName)1473   public static String getUserAgent(Context context, String applicationName) {
1474     String versionName;
1475     try {
1476       String packageName = context.getPackageName();
1477       PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);
1478       versionName = info.versionName;
1479     } catch (NameNotFoundException e) {
1480       versionName = "?";
1481     }
1482     return applicationName
1483         + "/"
1484         + versionName
1485         + " (Linux;Android "
1486         + Build.VERSION.RELEASE
1487         + ") "
1488         + ExoPlayerLibraryInfo.VERSION_SLASHY;
1489   }
1490 
1491   /** Returns the number of codec strings in {@code codecs} whose type matches {@code trackType}. */
getCodecCountOfType(@ullable String codecs, @C.TrackType int trackType)1492   public static int getCodecCountOfType(@Nullable String codecs, @C.TrackType int trackType) {
1493     String[] codecArray = splitCodecs(codecs);
1494     int count = 0;
1495     for (String codec : codecArray) {
1496       if (trackType == MimeTypes.getTrackTypeOfCodec(codec)) {
1497         count++;
1498       }
1499     }
1500     return count;
1501   }
1502 
1503   /**
1504    * Returns a copy of {@code codecs} without the codecs whose track type doesn't match {@code
1505    * trackType}.
1506    *
1507    * @param codecs A codec sequence string, as defined in RFC 6381.
1508    * @param trackType The {@link C.TrackType track type}.
1509    * @return A copy of {@code codecs} without the codecs whose track type doesn't match {@code
1510    *     trackType}. If this ends up empty, or {@code codecs} is null, returns null.
1511    */
1512   @Nullable
getCodecsOfType(@ullable String codecs, @C.TrackType int trackType)1513   public static String getCodecsOfType(@Nullable String codecs, @C.TrackType int trackType) {
1514     String[] codecArray = splitCodecs(codecs);
1515     if (codecArray.length == 0) {
1516       return null;
1517     }
1518     StringBuilder builder = new StringBuilder();
1519     for (String codec : codecArray) {
1520       if (trackType == MimeTypes.getTrackTypeOfCodec(codec)) {
1521         if (builder.length() > 0) {
1522           builder.append(",");
1523         }
1524         builder.append(codec);
1525       }
1526     }
1527     return builder.length() > 0 ? builder.toString() : null;
1528   }
1529 
1530   /**
1531    * Splits a codecs sequence string, as defined in RFC 6381, into individual codec strings.
1532    *
1533    * @param codecs A codec sequence string, as defined in RFC 6381.
1534    * @return The split codecs, or an array of length zero if the input was empty or null.
1535    */
splitCodecs(@ullable String codecs)1536   public static String[] splitCodecs(@Nullable String codecs) {
1537     if (TextUtils.isEmpty(codecs)) {
1538       return new String[0];
1539     }
1540     return split(codecs.trim(), "(\\s*,\\s*)");
1541   }
1542 
1543   /**
1544    * Gets a PCM {@link Format} with the specified parameters.
1545    *
1546    * @param pcmEncoding The {@link C.PcmEncoding}.
1547    * @param channels The number of channels, or {@link Format#NO_VALUE} if unknown.
1548    * @param sampleRate The sample rate in Hz, or {@link Format#NO_VALUE} if unknown.
1549    * @return The PCM format.
1550    */
getPcmFormat(@.PcmEncoding int pcmEncoding, int channels, int sampleRate)1551   public static Format getPcmFormat(@C.PcmEncoding int pcmEncoding, int channels, int sampleRate) {
1552     return new Format.Builder()
1553         .setSampleMimeType(MimeTypes.AUDIO_RAW)
1554         .setChannelCount(channels)
1555         .setSampleRate(sampleRate)
1556         .setPcmEncoding(pcmEncoding)
1557         .build();
1558   }
1559 
1560   /**
1561    * Converts a sample bit depth to a corresponding PCM encoding constant.
1562    *
1563    * @param bitDepth The bit depth. Supported values are 8, 16, 24 and 32.
1564    * @return The corresponding encoding. One of {@link C#ENCODING_PCM_8BIT}, {@link
1565    *     C#ENCODING_PCM_16BIT}, {@link C#ENCODING_PCM_24BIT} and {@link C#ENCODING_PCM_32BIT}. If
1566    *     the bit depth is unsupported then {@link C#ENCODING_INVALID} is returned.
1567    */
getPcmEncoding(int bitDepth)1568   public static @C.PcmEncoding int getPcmEncoding(int bitDepth) {
1569     switch (bitDepth) {
1570       case 8:
1571         return C.ENCODING_PCM_8BIT;
1572       case 16:
1573         return C.ENCODING_PCM_16BIT;
1574       case 24:
1575         return C.ENCODING_PCM_24BIT;
1576       case 32:
1577         return C.ENCODING_PCM_32BIT;
1578       default:
1579         return C.ENCODING_INVALID;
1580     }
1581   }
1582 
1583   /**
1584    * Returns whether {@code encoding} is one of the linear PCM encodings.
1585    *
1586    * @param encoding The encoding of the audio data.
1587    * @return Whether the encoding is one of the PCM encodings.
1588    */
isEncodingLinearPcm(@.Encoding int encoding)1589   public static boolean isEncodingLinearPcm(@C.Encoding int encoding) {
1590     return encoding == C.ENCODING_PCM_8BIT
1591         || encoding == C.ENCODING_PCM_16BIT
1592         || encoding == C.ENCODING_PCM_16BIT_BIG_ENDIAN
1593         || encoding == C.ENCODING_PCM_24BIT
1594         || encoding == C.ENCODING_PCM_32BIT
1595         || encoding == C.ENCODING_PCM_FLOAT;
1596   }
1597 
1598   /**
1599    * Returns whether {@code encoding} is high resolution (&gt; 16-bit) PCM.
1600    *
1601    * @param encoding The encoding of the audio data.
1602    * @return Whether the encoding is high resolution PCM.
1603    */
isEncodingHighResolutionPcm(@.PcmEncoding int encoding)1604   public static boolean isEncodingHighResolutionPcm(@C.PcmEncoding int encoding) {
1605     return encoding == C.ENCODING_PCM_24BIT
1606         || encoding == C.ENCODING_PCM_32BIT
1607         || encoding == C.ENCODING_PCM_FLOAT;
1608   }
1609 
1610   /**
1611    * Returns the audio track channel configuration for the given channel count, or {@link
1612    * AudioFormat#CHANNEL_INVALID} if output is not possible.
1613    *
1614    * @param channelCount The number of channels in the input audio.
1615    * @return The channel configuration or {@link AudioFormat#CHANNEL_INVALID} if output is not
1616    *     possible.
1617    */
getAudioTrackChannelConfig(int channelCount)1618   public static int getAudioTrackChannelConfig(int channelCount) {
1619     switch (channelCount) {
1620       case 1:
1621         return AudioFormat.CHANNEL_OUT_MONO;
1622       case 2:
1623         return AudioFormat.CHANNEL_OUT_STEREO;
1624       case 3:
1625         return AudioFormat.CHANNEL_OUT_STEREO | AudioFormat.CHANNEL_OUT_FRONT_CENTER;
1626       case 4:
1627         return AudioFormat.CHANNEL_OUT_QUAD;
1628       case 5:
1629         return AudioFormat.CHANNEL_OUT_QUAD | AudioFormat.CHANNEL_OUT_FRONT_CENTER;
1630       case 6:
1631         return AudioFormat.CHANNEL_OUT_5POINT1;
1632       case 7:
1633         return AudioFormat.CHANNEL_OUT_5POINT1 | AudioFormat.CHANNEL_OUT_BACK_CENTER;
1634       case 8:
1635         if (Util.SDK_INT >= 23) {
1636           return AudioFormat.CHANNEL_OUT_7POINT1_SURROUND;
1637         } else if (Util.SDK_INT >= 21) {
1638           // Equal to AudioFormat.CHANNEL_OUT_7POINT1_SURROUND, which is hidden before Android M.
1639           return AudioFormat.CHANNEL_OUT_5POINT1
1640               | AudioFormat.CHANNEL_OUT_SIDE_LEFT
1641               | AudioFormat.CHANNEL_OUT_SIDE_RIGHT;
1642         } else {
1643           // 8 ch output is not supported before Android L.
1644           return AudioFormat.CHANNEL_INVALID;
1645         }
1646       default:
1647         return AudioFormat.CHANNEL_INVALID;
1648     }
1649   }
1650 
1651   /**
1652    * Returns the frame size for audio with {@code channelCount} channels in the specified encoding.
1653    *
1654    * @param pcmEncoding The encoding of the audio data.
1655    * @param channelCount The channel count.
1656    * @return The size of one audio frame in bytes.
1657    */
getPcmFrameSize(@.PcmEncoding int pcmEncoding, int channelCount)1658   public static int getPcmFrameSize(@C.PcmEncoding int pcmEncoding, int channelCount) {
1659     switch (pcmEncoding) {
1660       case C.ENCODING_PCM_8BIT:
1661         return channelCount;
1662       case C.ENCODING_PCM_16BIT:
1663       case C.ENCODING_PCM_16BIT_BIG_ENDIAN:
1664         return channelCount * 2;
1665       case C.ENCODING_PCM_24BIT:
1666         return channelCount * 3;
1667       case C.ENCODING_PCM_32BIT:
1668       case C.ENCODING_PCM_FLOAT:
1669         return channelCount * 4;
1670       case C.ENCODING_INVALID:
1671       case Format.NO_VALUE:
1672       default:
1673         throw new IllegalArgumentException();
1674     }
1675   }
1676 
1677   /** Returns the {@link C.AudioUsage} corresponding to the specified {@link C.StreamType}. */
getAudioUsageForStreamType(@.StreamType int streamType)1678   public static @C.AudioUsage int getAudioUsageForStreamType(@C.StreamType int streamType) {
1679     switch (streamType) {
1680       case C.STREAM_TYPE_ALARM:
1681         return C.USAGE_ALARM;
1682       case C.STREAM_TYPE_DTMF:
1683         return C.USAGE_VOICE_COMMUNICATION_SIGNALLING;
1684       case C.STREAM_TYPE_NOTIFICATION:
1685         return C.USAGE_NOTIFICATION;
1686       case C.STREAM_TYPE_RING:
1687         return C.USAGE_NOTIFICATION_RINGTONE;
1688       case C.STREAM_TYPE_SYSTEM:
1689         return C.USAGE_ASSISTANCE_SONIFICATION;
1690       case C.STREAM_TYPE_VOICE_CALL:
1691         return C.USAGE_VOICE_COMMUNICATION;
1692       case C.STREAM_TYPE_MUSIC:
1693       default:
1694         return C.USAGE_MEDIA;
1695     }
1696   }
1697 
1698   /** Returns the {@link C.AudioContentType} corresponding to the specified {@link C.StreamType}. */
getAudioContentTypeForStreamType( @.StreamType int streamType)1699   public static @C.AudioContentType int getAudioContentTypeForStreamType(
1700       @C.StreamType int streamType) {
1701     switch (streamType) {
1702       case C.STREAM_TYPE_ALARM:
1703       case C.STREAM_TYPE_DTMF:
1704       case C.STREAM_TYPE_NOTIFICATION:
1705       case C.STREAM_TYPE_RING:
1706       case C.STREAM_TYPE_SYSTEM:
1707         return C.CONTENT_TYPE_SONIFICATION;
1708       case C.STREAM_TYPE_VOICE_CALL:
1709         return C.CONTENT_TYPE_SPEECH;
1710       case C.STREAM_TYPE_MUSIC:
1711       default:
1712         return C.CONTENT_TYPE_MUSIC;
1713     }
1714   }
1715 
1716   /** Returns the {@link C.StreamType} corresponding to the specified {@link C.AudioUsage}. */
getStreamTypeForAudioUsage(@.AudioUsage int usage)1717   public static @C.StreamType int getStreamTypeForAudioUsage(@C.AudioUsage int usage) {
1718     switch (usage) {
1719       case C.USAGE_MEDIA:
1720       case C.USAGE_GAME:
1721       case C.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE:
1722         return C.STREAM_TYPE_MUSIC;
1723       case C.USAGE_ASSISTANCE_SONIFICATION:
1724         return C.STREAM_TYPE_SYSTEM;
1725       case C.USAGE_VOICE_COMMUNICATION:
1726         return C.STREAM_TYPE_VOICE_CALL;
1727       case C.USAGE_VOICE_COMMUNICATION_SIGNALLING:
1728         return C.STREAM_TYPE_DTMF;
1729       case C.USAGE_ALARM:
1730         return C.STREAM_TYPE_ALARM;
1731       case C.USAGE_NOTIFICATION_RINGTONE:
1732         return C.STREAM_TYPE_RING;
1733       case C.USAGE_NOTIFICATION:
1734       case C.USAGE_NOTIFICATION_COMMUNICATION_REQUEST:
1735       case C.USAGE_NOTIFICATION_COMMUNICATION_INSTANT:
1736       case C.USAGE_NOTIFICATION_COMMUNICATION_DELAYED:
1737       case C.USAGE_NOTIFICATION_EVENT:
1738         return C.STREAM_TYPE_NOTIFICATION;
1739       case C.USAGE_ASSISTANCE_ACCESSIBILITY:
1740       case C.USAGE_ASSISTANT:
1741       case C.USAGE_UNKNOWN:
1742       default:
1743         return C.STREAM_TYPE_DEFAULT;
1744     }
1745   }
1746 
1747   /**
1748    * Returns a newly generated audio session identifier, or {@link AudioManager#ERROR} if an error
1749    * occurred in which case audio playback may fail.
1750    *
1751    * @see AudioManager#generateAudioSessionId()
1752    */
1753   @RequiresApi(21)
generateAudioSessionIdV21(Context context)1754   public static int generateAudioSessionIdV21(Context context) {
1755     @Nullable
1756     AudioManager audioManager = ((AudioManager) context.getSystemService(Context.AUDIO_SERVICE));
1757     return audioManager == null ? AudioManager.ERROR : audioManager.generateAudioSessionId();
1758   }
1759 
1760   /**
1761    * Derives a DRM {@link UUID} from {@code drmScheme}.
1762    *
1763    * @param drmScheme A UUID string, or {@code "widevine"}, {@code "playready"} or {@code
1764    *     "clearkey"}.
1765    * @return The derived {@link UUID}, or {@code null} if one could not be derived.
1766    */
1767   @Nullable
getDrmUuid(String drmScheme)1768   public static UUID getDrmUuid(String drmScheme) {
1769     switch (Ascii.toLowerCase(drmScheme)) {
1770       case "widevine":
1771         return C.WIDEVINE_UUID;
1772       case "playready":
1773         return C.PLAYREADY_UUID;
1774       case "clearkey":
1775         return C.CLEARKEY_UUID;
1776       default:
1777         try {
1778           return UUID.fromString(drmScheme);
1779         } catch (RuntimeException e) {
1780           return null;
1781         }
1782     }
1783   }
1784 
1785   /**
1786    * Returns a {@link PlaybackException.ErrorCode} value that corresponds to the provided {@link
1787    * MediaDrm.ErrorCodes} value. Returns {@link PlaybackException#ERROR_CODE_DRM_SYSTEM_ERROR} if
1788    * the provided error code isn't recognised.
1789    */
getErrorCodeForMediaDrmErrorCode( int mediaDrmErrorCode)1790   public static @PlaybackException.ErrorCode int getErrorCodeForMediaDrmErrorCode(
1791       int mediaDrmErrorCode) {
1792     switch (mediaDrmErrorCode) {
1793       case MediaDrm.ErrorCodes.ERROR_PROVISIONING_CONFIG:
1794       case MediaDrm.ErrorCodes.ERROR_PROVISIONING_PARSE:
1795       case MediaDrm.ErrorCodes.ERROR_PROVISIONING_REQUEST_REJECTED:
1796       case MediaDrm.ErrorCodes.ERROR_PROVISIONING_CERTIFICATE:
1797       case MediaDrm.ErrorCodes.ERROR_PROVISIONING_RETRY:
1798         return PlaybackException.ERROR_CODE_DRM_PROVISIONING_FAILED;
1799       case MediaDrm.ErrorCodes.ERROR_LICENSE_PARSE:
1800       case MediaDrm.ErrorCodes.ERROR_LICENSE_RELEASE:
1801       case MediaDrm.ErrorCodes.ERROR_LICENSE_REQUEST_REJECTED:
1802       case MediaDrm.ErrorCodes.ERROR_LICENSE_RESTORE:
1803       case MediaDrm.ErrorCodes.ERROR_LICENSE_STATE:
1804       case MediaDrm.ErrorCodes.ERROR_CERTIFICATE_MALFORMED:
1805         return PlaybackException.ERROR_CODE_DRM_LICENSE_ACQUISITION_FAILED;
1806       case MediaDrm.ErrorCodes.ERROR_LICENSE_POLICY:
1807       case MediaDrm.ErrorCodes.ERROR_INSUFFICIENT_OUTPUT_PROTECTION:
1808       case MediaDrm.ErrorCodes.ERROR_INSUFFICIENT_SECURITY:
1809       case MediaDrm.ErrorCodes.ERROR_KEY_EXPIRED:
1810       case MediaDrm.ErrorCodes.ERROR_KEY_NOT_LOADED:
1811         return PlaybackException.ERROR_CODE_DRM_DISALLOWED_OPERATION;
1812       case MediaDrm.ErrorCodes.ERROR_INIT_DATA:
1813       case MediaDrm.ErrorCodes.ERROR_FRAME_TOO_LARGE:
1814         return PlaybackException.ERROR_CODE_DRM_CONTENT_ERROR;
1815       default:
1816         return PlaybackException.ERROR_CODE_DRM_SYSTEM_ERROR;
1817     }
1818   }
1819 
1820   /**
1821    * Makes a best guess to infer the {@link ContentType} from a {@link Uri}.
1822    *
1823    * @param uri The {@link Uri}.
1824    * @param overrideExtension If not null, used to infer the type.
1825    * @return The content type.
1826    */
inferContentType(Uri uri, @Nullable String overrideExtension)1827   public static @ContentType int inferContentType(Uri uri, @Nullable String overrideExtension) {
1828     return TextUtils.isEmpty(overrideExtension)
1829         ? inferContentType(uri)
1830         : inferContentType("." + overrideExtension);
1831   }
1832 
1833   /**
1834    * Makes a best guess to infer the {@link ContentType} from a {@link Uri}.
1835    *
1836    * @param uri The {@link Uri}.
1837    * @return The content type.
1838    */
inferContentType(Uri uri)1839   public static @ContentType int inferContentType(Uri uri) {
1840     @Nullable String scheme = uri.getScheme();
1841     if (scheme != null && Ascii.equalsIgnoreCase("rtsp", scheme)) {
1842       return C.TYPE_RTSP;
1843     }
1844 
1845     @Nullable String path = uri.getPath();
1846     return path == null ? C.TYPE_OTHER : inferContentType(path);
1847   }
1848 
1849   /**
1850    * Makes a best guess to infer the {@link ContentType} from a file name.
1851    *
1852    * @param fileName Name of the file. It can include the path of the file.
1853    * @return The content type.
1854    */
inferContentType(String fileName)1855   public static @ContentType int inferContentType(String fileName) {
1856     fileName = Ascii.toLowerCase(fileName);
1857     if (fileName.endsWith(".mpd")) {
1858       return C.TYPE_DASH;
1859     } else if (fileName.endsWith(".m3u8")) {
1860       return C.TYPE_HLS;
1861     }
1862     Matcher ismMatcher = ISM_URL_PATTERN.matcher(fileName);
1863     if (ismMatcher.matches()) {
1864       @Nullable String extensions = ismMatcher.group(2);
1865       if (extensions != null) {
1866         if (extensions.contains(ISM_DASH_FORMAT_EXTENSION)) {
1867           return C.TYPE_DASH;
1868         } else if (extensions.contains(ISM_HLS_FORMAT_EXTENSION)) {
1869           return C.TYPE_HLS;
1870         }
1871       }
1872       return C.TYPE_SS;
1873     }
1874     return C.TYPE_OTHER;
1875   }
1876 
1877   /**
1878    * Makes a best guess to infer the {@link ContentType} from a {@link Uri} and optional MIME type.
1879    *
1880    * @param uri The {@link Uri}.
1881    * @param mimeType If MIME type, or {@code null}.
1882    * @return The content type.
1883    */
inferContentTypeForUriAndMimeType( Uri uri, @Nullable String mimeType)1884   public static @ContentType int inferContentTypeForUriAndMimeType(
1885       Uri uri, @Nullable String mimeType) {
1886     if (mimeType == null) {
1887       return Util.inferContentType(uri);
1888     }
1889     switch (mimeType) {
1890       case MimeTypes.APPLICATION_MPD:
1891         return C.TYPE_DASH;
1892       case MimeTypes.APPLICATION_M3U8:
1893         return C.TYPE_HLS;
1894       case MimeTypes.APPLICATION_SS:
1895         return C.TYPE_SS;
1896       case MimeTypes.APPLICATION_RTSP:
1897         return C.TYPE_RTSP;
1898       default:
1899         return C.TYPE_OTHER;
1900     }
1901   }
1902 
1903   /**
1904    * Returns the MIME type corresponding to the given adaptive {@link ContentType}, or {@code null}
1905    * if the content type is not adaptive.
1906    */
1907   @Nullable
getAdaptiveMimeTypeForContentType(@ontentType int contentType)1908   public static String getAdaptiveMimeTypeForContentType(@ContentType int contentType) {
1909     switch (contentType) {
1910       case C.TYPE_DASH:
1911         return MimeTypes.APPLICATION_MPD;
1912       case C.TYPE_HLS:
1913         return MimeTypes.APPLICATION_M3U8;
1914       case C.TYPE_SS:
1915         return MimeTypes.APPLICATION_SS;
1916       case C.TYPE_RTSP:
1917       case C.TYPE_OTHER:
1918       default:
1919         return null;
1920     }
1921   }
1922 
1923   /**
1924    * If the provided URI is an ISM Presentation URI, returns the URI with "Manifest" appended to its
1925    * path (i.e., the corresponding default manifest URI). Else returns the provided URI without
1926    * modification. See [MS-SSTR] v20180912, section 2.2.1.
1927    *
1928    * @param uri The original URI.
1929    * @return The fixed URI.
1930    */
fixSmoothStreamingIsmManifestUri(Uri uri)1931   public static Uri fixSmoothStreamingIsmManifestUri(Uri uri) {
1932     @Nullable String path = uri.getPath();
1933     if (path == null) {
1934       return uri;
1935     }
1936     Matcher ismMatcher = ISM_URL_PATTERN.matcher(Ascii.toLowerCase(path));
1937     if (ismMatcher.matches() && ismMatcher.group(1) == null) {
1938       // Add missing "Manifest" suffix.
1939       return Uri.withAppendedPath(uri, "Manifest");
1940     }
1941     return uri;
1942   }
1943 
1944   /**
1945    * Returns the specified millisecond time formatted as a string.
1946    *
1947    * @param builder The builder that {@code formatter} will write to.
1948    * @param formatter The formatter.
1949    * @param timeMs The time to format as a string, in milliseconds.
1950    * @return The time formatted as a string.
1951    */
getStringForTime(StringBuilder builder, Formatter formatter, long timeMs)1952   public static String getStringForTime(StringBuilder builder, Formatter formatter, long timeMs) {
1953     if (timeMs == C.TIME_UNSET) {
1954       timeMs = 0;
1955     }
1956     String prefix = timeMs < 0 ? "-" : "";
1957     timeMs = abs(timeMs);
1958     long totalSeconds = (timeMs + 500) / 1000;
1959     long seconds = totalSeconds % 60;
1960     long minutes = (totalSeconds / 60) % 60;
1961     long hours = totalSeconds / 3600;
1962     builder.setLength(0);
1963     return hours > 0
1964         ? formatter.format("%s%d:%02d:%02d", prefix, hours, minutes, seconds).toString()
1965         : formatter.format("%s%02d:%02d", prefix, minutes, seconds).toString();
1966   }
1967 
1968   /**
1969    * Escapes a string so that it's safe for use as a file or directory name on at least FAT32
1970    * filesystems. FAT32 is the most restrictive of all filesystems still commonly used today.
1971    *
1972    * <p>For simplicity, this only handles common characters known to be illegal on FAT32: &lt;,
1973    * &gt;, :, ", /, \, |, ?, and *. % is also escaped since it is used as the escape character.
1974    * Escaping is performed in a consistent way so that no collisions occur and {@link
1975    * #unescapeFileName(String)} can be used to retrieve the original file name.
1976    *
1977    * @param fileName File name to be escaped.
1978    * @return An escaped file name which will be safe for use on at least FAT32 filesystems.
1979    */
escapeFileName(String fileName)1980   public static String escapeFileName(String fileName) {
1981     int length = fileName.length();
1982     int charactersToEscapeCount = 0;
1983     for (int i = 0; i < length; i++) {
1984       if (shouldEscapeCharacter(fileName.charAt(i))) {
1985         charactersToEscapeCount++;
1986       }
1987     }
1988     if (charactersToEscapeCount == 0) {
1989       return fileName;
1990     }
1991 
1992     int i = 0;
1993     StringBuilder builder = new StringBuilder(length + charactersToEscapeCount * 2);
1994     while (charactersToEscapeCount > 0) {
1995       char c = fileName.charAt(i++);
1996       if (shouldEscapeCharacter(c)) {
1997         builder.append('%').append(Integer.toHexString(c));
1998         charactersToEscapeCount--;
1999       } else {
2000         builder.append(c);
2001       }
2002     }
2003     if (i < length) {
2004       builder.append(fileName, i, length);
2005     }
2006     return builder.toString();
2007   }
2008 
shouldEscapeCharacter(char c)2009   private static boolean shouldEscapeCharacter(char c) {
2010     switch (c) {
2011       case '<':
2012       case '>':
2013       case ':':
2014       case '"':
2015       case '/':
2016       case '\\':
2017       case '|':
2018       case '?':
2019       case '*':
2020       case '%':
2021         return true;
2022       default:
2023         return false;
2024     }
2025   }
2026 
2027   /**
2028    * Unescapes an escaped file or directory name back to its original value.
2029    *
2030    * <p>See {@link #escapeFileName(String)} for more information.
2031    *
2032    * @param fileName File name to be unescaped.
2033    * @return The original value of the file name before it was escaped, or null if the escaped
2034    *     fileName seems invalid.
2035    */
2036   @Nullable
unescapeFileName(String fileName)2037   public static String unescapeFileName(String fileName) {
2038     int length = fileName.length();
2039     int percentCharacterCount = 0;
2040     for (int i = 0; i < length; i++) {
2041       if (fileName.charAt(i) == '%') {
2042         percentCharacterCount++;
2043       }
2044     }
2045     if (percentCharacterCount == 0) {
2046       return fileName;
2047     }
2048 
2049     int expectedLength = length - percentCharacterCount * 2;
2050     StringBuilder builder = new StringBuilder(expectedLength);
2051     Matcher matcher = ESCAPED_CHARACTER_PATTERN.matcher(fileName);
2052     int startOfNotEscaped = 0;
2053     while (percentCharacterCount > 0 && matcher.find()) {
2054       char unescapedCharacter = (char) Integer.parseInt(checkNotNull(matcher.group(1)), 16);
2055       builder.append(fileName, startOfNotEscaped, matcher.start()).append(unescapedCharacter);
2056       startOfNotEscaped = matcher.end();
2057       percentCharacterCount--;
2058     }
2059     if (startOfNotEscaped < length) {
2060       builder.append(fileName, startOfNotEscaped, length);
2061     }
2062     if (builder.length() != expectedLength) {
2063       return null;
2064     }
2065     return builder.toString();
2066   }
2067 
2068   /** Returns a data URI with the specified MIME type and data. */
getDataUriForString(String mimeType, String data)2069   public static Uri getDataUriForString(String mimeType, String data) {
2070     return Uri.parse(
2071         "data:" + mimeType + ";base64," + Base64.encodeToString(data.getBytes(), Base64.NO_WRAP));
2072   }
2073 
2074   /**
2075    * A hacky method that always throws {@code t} even if {@code t} is a checked exception, and is
2076    * not declared to be thrown.
2077    */
sneakyThrow(Throwable t)2078   public static void sneakyThrow(Throwable t) {
2079     sneakyThrowInternal(t);
2080   }
2081 
2082   @SuppressWarnings("unchecked")
sneakyThrowInternal(Throwable t)2083   private static <T extends Throwable> void sneakyThrowInternal(Throwable t) throws T {
2084     throw (T) t;
2085   }
2086 
2087   /** Recursively deletes a directory and its content. */
recursiveDelete(File fileOrDirectory)2088   public static void recursiveDelete(File fileOrDirectory) {
2089     File[] directoryFiles = fileOrDirectory.listFiles();
2090     if (directoryFiles != null) {
2091       for (File child : directoryFiles) {
2092         recursiveDelete(child);
2093       }
2094     }
2095     fileOrDirectory.delete();
2096   }
2097 
2098   /** Creates an empty directory in the directory returned by {@link Context#getCacheDir()}. */
createTempDirectory(Context context, String prefix)2099   public static File createTempDirectory(Context context, String prefix) throws IOException {
2100     File tempFile = createTempFile(context, prefix);
2101     tempFile.delete(); // Delete the temp file.
2102     tempFile.mkdir(); // Create a directory with the same name.
2103     return tempFile;
2104   }
2105 
2106   /** Creates a new empty file in the directory returned by {@link Context#getCacheDir()}. */
createTempFile(Context context, String prefix)2107   public static File createTempFile(Context context, String prefix) throws IOException {
2108     return File.createTempFile(prefix, null, checkNotNull(context.getCacheDir()));
2109   }
2110 
2111   /**
2112    * Returns the result of updating a CRC-32 with the specified bytes in a "most significant bit
2113    * first" order.
2114    *
2115    * @param bytes Array containing the bytes to update the crc value with.
2116    * @param start The index to the first byte in the byte range to update the crc with.
2117    * @param end The index after the last byte in the byte range to update the crc with.
2118    * @param initialValue The initial value for the crc calculation.
2119    * @return The result of updating the initial value with the specified bytes.
2120    */
crc32(byte[] bytes, int start, int end, int initialValue)2121   public static int crc32(byte[] bytes, int start, int end, int initialValue) {
2122     for (int i = start; i < end; i++) {
2123       initialValue =
2124           (initialValue << 8)
2125               ^ CRC32_BYTES_MSBF[((initialValue >>> 24) ^ (bytes[i] & 0xFF)) & 0xFF];
2126     }
2127     return initialValue;
2128   }
2129 
2130   /**
2131    * Returns the result of updating a CRC-8 with the specified bytes in a "most significant bit
2132    * first" order.
2133    *
2134    * @param bytes Array containing the bytes to update the crc value with.
2135    * @param start The index to the first byte in the byte range to update the crc with.
2136    * @param end The index after the last byte in the byte range to update the crc with.
2137    * @param initialValue The initial value for the crc calculation.
2138    * @return The result of updating the initial value with the specified bytes.
2139    */
crc8(byte[] bytes, int start, int end, int initialValue)2140   public static int crc8(byte[] bytes, int start, int end, int initialValue) {
2141     for (int i = start; i < end; i++) {
2142       initialValue = CRC8_BYTES_MSBF[initialValue ^ (bytes[i] & 0xFF)];
2143     }
2144     return initialValue;
2145   }
2146 
2147   /** Compresses {@code input} using gzip and returns the result in a newly allocated byte array. */
gzip(byte[] input)2148   public static byte[] gzip(byte[] input) {
2149     ByteArrayOutputStream output = new ByteArrayOutputStream();
2150     try (GZIPOutputStream os = new GZIPOutputStream(output)) {
2151       os.write(input);
2152     } catch (IOException e) {
2153       // A ByteArrayOutputStream wrapped in a GZipOutputStream should never throw IOException since
2154       // no I/O is happening.
2155       throw new IllegalStateException(e);
2156     }
2157     return output.toByteArray();
2158   }
2159 
2160   /**
2161    * Absolute <i>get</i> method for reading an int value in {@link ByteOrder#BIG_ENDIAN} in a {@link
2162    * ByteBuffer}. Same as {@link ByteBuffer#getInt(int)} except the buffer's order as returned by
2163    * {@link ByteBuffer#order()} is ignored and {@link ByteOrder#BIG_ENDIAN} is used instead.
2164    *
2165    * @param buffer The buffer from which to read an int in big endian.
2166    * @param index The index from which the bytes will be read.
2167    * @return The int value at the given index with the buffer bytes ordered most significant to
2168    *     least significant.
2169    */
getBigEndianInt(ByteBuffer buffer, int index)2170   public static int getBigEndianInt(ByteBuffer buffer, int index) {
2171     int value = buffer.getInt(index);
2172     return buffer.order() == ByteOrder.BIG_ENDIAN ? value : Integer.reverseBytes(value);
2173   }
2174 
2175   /**
2176    * Returns the upper-case ISO 3166-1 alpha-2 country code of the current registered operator's MCC
2177    * (Mobile Country Code), or the country code of the default Locale if not available.
2178    *
2179    * @param context A context to access the telephony service. If null, only the Locale can be used.
2180    * @return The upper-case ISO 3166-1 alpha-2 country code, or an empty String if unavailable.
2181    */
getCountryCode(@ullable Context context)2182   public static String getCountryCode(@Nullable Context context) {
2183     if (context != null) {
2184       @Nullable
2185       TelephonyManager telephonyManager =
2186           (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
2187       if (telephonyManager != null) {
2188         String countryCode = telephonyManager.getNetworkCountryIso();
2189         if (!TextUtils.isEmpty(countryCode)) {
2190           return Ascii.toUpperCase(countryCode);
2191         }
2192       }
2193     }
2194     return Ascii.toUpperCase(Locale.getDefault().getCountry());
2195   }
2196 
2197   /**
2198    * Returns a non-empty array of normalized IETF BCP 47 language tags for the system languages
2199    * ordered by preference.
2200    */
getSystemLanguageCodes()2201   public static String[] getSystemLanguageCodes() {
2202     String[] systemLocales = getSystemLocales();
2203     for (int i = 0; i < systemLocales.length; i++) {
2204       systemLocales[i] = normalizeLanguageCode(systemLocales[i]);
2205     }
2206     return systemLocales;
2207   }
2208 
2209   /** Returns the default {@link Locale.Category#DISPLAY DISPLAY} {@link Locale}. */
getDefaultDisplayLocale()2210   public static Locale getDefaultDisplayLocale() {
2211     return Util.SDK_INT >= 24 ? Locale.getDefault(Locale.Category.DISPLAY) : Locale.getDefault();
2212   }
2213 
2214   /**
2215    * Uncompresses the data in {@code input}.
2216    *
2217    * @param input Wraps the compressed input data.
2218    * @param output Wraps an output buffer to be used to store the uncompressed data. If {@code
2219    *     output.data} isn't big enough to hold the uncompressed data, a new array is created. If
2220    *     {@code true} is returned then the output's position will be set to 0 and its limit will be
2221    *     set to the length of the uncompressed data.
2222    * @param inflater If not null, used to uncompressed the input. Otherwise a new {@link Inflater}
2223    *     is created.
2224    * @return Whether the input is uncompressed successfully.
2225    */
inflate( ParsableByteArray input, ParsableByteArray output, @Nullable Inflater inflater)2226   public static boolean inflate(
2227       ParsableByteArray input, ParsableByteArray output, @Nullable Inflater inflater) {
2228     if (input.bytesLeft() <= 0) {
2229       return false;
2230     }
2231     if (output.capacity() < input.bytesLeft()) {
2232       output.ensureCapacity(2 * input.bytesLeft());
2233     }
2234     if (inflater == null) {
2235       inflater = new Inflater();
2236     }
2237     inflater.setInput(input.getData(), input.getPosition(), input.bytesLeft());
2238     try {
2239       int outputSize = 0;
2240       while (true) {
2241         outputSize +=
2242             inflater.inflate(output.getData(), outputSize, output.capacity() - outputSize);
2243         if (inflater.finished()) {
2244           output.setLimit(outputSize);
2245           return true;
2246         }
2247         if (inflater.needsDictionary() || inflater.needsInput()) {
2248           return false;
2249         }
2250         if (outputSize == output.capacity()) {
2251           output.ensureCapacity(output.capacity() * 2);
2252         }
2253       }
2254     } catch (DataFormatException e) {
2255       return false;
2256     } finally {
2257       inflater.reset();
2258     }
2259   }
2260 
2261   /**
2262    * Returns whether the app is running on a TV device.
2263    *
2264    * @param context Any context.
2265    * @return Whether the app is running on a TV device.
2266    */
isTv(Context context)2267   public static boolean isTv(Context context) {
2268     // See https://developer.android.com/training/tv/start/hardware.html#runtime-check.
2269     @Nullable
2270     UiModeManager uiModeManager =
2271         (UiModeManager) context.getApplicationContext().getSystemService(UI_MODE_SERVICE);
2272     return uiModeManager != null
2273         && uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_TELEVISION;
2274   }
2275 
2276   /**
2277    * Returns whether the app is running on an automotive device.
2278    *
2279    * @param context Any context.
2280    * @return Whether the app is running on an automotive device.
2281    */
isAutomotive(Context context)2282   public static boolean isAutomotive(Context context) {
2283     return Util.SDK_INT >= 23
2284         && context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
2285   }
2286 
2287   /**
2288    * Gets the size of the current mode of the default display, in pixels.
2289    *
2290    * <p>Note that due to application UI scaling, the number of pixels made available to applications
2291    * (as reported by {@link Display#getSize(Point)} may differ from the mode's actual resolution (as
2292    * reported by this function). For example, applications running on a display configured with a 4K
2293    * mode may have their UI laid out and rendered in 1080p and then scaled up. Applications can take
2294    * advantage of the full mode resolution through a {@link SurfaceView} using full size buffers.
2295    *
2296    * @param context Any context.
2297    * @return The size of the current mode, in pixels.
2298    */
getCurrentDisplayModeSize(Context context)2299   public static Point getCurrentDisplayModeSize(Context context) {
2300     @Nullable Display defaultDisplay = null;
2301     if (Util.SDK_INT >= 17) {
2302       @Nullable
2303       DisplayManager displayManager =
2304           (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
2305       // We don't expect displayManager to ever be null, so this check is just precautionary.
2306       // Consider removing it when the library minSdkVersion is increased to 17 or higher.
2307       if (displayManager != null) {
2308         defaultDisplay = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
2309       }
2310     }
2311     if (defaultDisplay == null) {
2312       WindowManager windowManager =
2313           checkNotNull((WindowManager) context.getSystemService(Context.WINDOW_SERVICE));
2314       defaultDisplay = windowManager.getDefaultDisplay();
2315     }
2316     return getCurrentDisplayModeSize(context, defaultDisplay);
2317   }
2318 
2319   /**
2320    * Gets the size of the current mode of the specified display, in pixels.
2321    *
2322    * <p>Note that due to application UI scaling, the number of pixels made available to applications
2323    * (as reported by {@link Display#getSize(Point)} may differ from the mode's actual resolution (as
2324    * reported by this function). For example, applications running on a display configured with a 4K
2325    * mode may have their UI laid out and rendered in 1080p and then scaled up. Applications can take
2326    * advantage of the full mode resolution through a {@link SurfaceView} using full size buffers.
2327    *
2328    * @param context Any context.
2329    * @param display The display whose size is to be returned.
2330    * @return The size of the current mode, in pixels.
2331    */
getCurrentDisplayModeSize(Context context, Display display)2332   public static Point getCurrentDisplayModeSize(Context context, Display display) {
2333     if (display.getDisplayId() == Display.DEFAULT_DISPLAY && isTv(context)) {
2334       // On Android TVs it's common for the UI to be driven at a lower resolution than the physical
2335       // resolution of the display (e.g., driving the UI at 1080p when the display is 4K).
2336       // SurfaceView outputs are still able to use the full physical resolution on such devices.
2337       //
2338       // Prior to API level 26, the Display object did not provide a way to obtain the true physical
2339       // resolution of the display. From API level 26, Display.getMode().getPhysical[Width|Height]
2340       // is expected to return the display's true physical resolution, but we still see devices
2341       // setting their hardware compositor output size incorrectly, which makes this unreliable.
2342       // Hence for TV devices, we try and read the display's true physical resolution from system
2343       // properties.
2344       //
2345       // From API level 28, Treble may prevent the system from writing sys.display-size, so we check
2346       // vendor.display-size instead.
2347       @Nullable
2348       String displaySize =
2349           Util.SDK_INT < 28
2350               ? getSystemProperty("sys.display-size")
2351               : getSystemProperty("vendor.display-size");
2352       // If we managed to read the display size, attempt to parse it.
2353       if (!TextUtils.isEmpty(displaySize)) {
2354         try {
2355           String[] displaySizeParts = split(displaySize.trim(), "x");
2356           if (displaySizeParts.length == 2) {
2357             int width = Integer.parseInt(displaySizeParts[0]);
2358             int height = Integer.parseInt(displaySizeParts[1]);
2359             if (width > 0 && height > 0) {
2360               return new Point(width, height);
2361             }
2362           }
2363         } catch (NumberFormatException e) {
2364           // Do nothing.
2365         }
2366         Log.e(TAG, "Invalid display size: " + displaySize);
2367       }
2368 
2369       // Sony Android TVs advertise support for 4k output via a system feature.
2370       if ("Sony".equals(Util.MANUFACTURER)
2371           && Util.MODEL.startsWith("BRAVIA")
2372           && context.getPackageManager().hasSystemFeature("com.sony.dtv.hardware.panel.qfhd")) {
2373         return new Point(3840, 2160);
2374       }
2375     }
2376 
2377     Point displaySize = new Point();
2378     if (Util.SDK_INT >= 23) {
2379       getDisplaySizeV23(display, displaySize);
2380     } else if (Util.SDK_INT >= 17) {
2381       getDisplaySizeV17(display, displaySize);
2382     } else {
2383       getDisplaySizeV16(display, displaySize);
2384     }
2385     return displaySize;
2386   }
2387 
2388   /**
2389    * Returns a string representation of a {@link C.TrackType}.
2390    *
2391    * @param trackType A {@link C.TrackType} constant,
2392    * @return A string representation of this constant.
2393    */
getTrackTypeString(@.TrackType int trackType)2394   public static String getTrackTypeString(@C.TrackType int trackType) {
2395     switch (trackType) {
2396       case C.TRACK_TYPE_DEFAULT:
2397         return "default";
2398       case C.TRACK_TYPE_AUDIO:
2399         return "audio";
2400       case C.TRACK_TYPE_VIDEO:
2401         return "video";
2402       case C.TRACK_TYPE_TEXT:
2403         return "text";
2404       case C.TRACK_TYPE_IMAGE:
2405         return "image";
2406       case C.TRACK_TYPE_METADATA:
2407         return "metadata";
2408       case C.TRACK_TYPE_CAMERA_MOTION:
2409         return "camera motion";
2410       case C.TRACK_TYPE_NONE:
2411         return "none";
2412       case C.TRACK_TYPE_UNKNOWN:
2413         return "unknown";
2414       default:
2415         return trackType >= C.TRACK_TYPE_CUSTOM_BASE ? "custom (" + trackType + ")" : "?";
2416     }
2417   }
2418 
2419   /**
2420    * Returns the current time in milliseconds since the epoch.
2421    *
2422    * @param elapsedRealtimeEpochOffsetMs The offset between {@link SystemClock#elapsedRealtime()}
2423    *     and the time since the Unix epoch, or {@link C#TIME_UNSET} if unknown.
2424    * @return The Unix time in milliseconds since the epoch.
2425    */
getNowUnixTimeMs(long elapsedRealtimeEpochOffsetMs)2426   public static long getNowUnixTimeMs(long elapsedRealtimeEpochOffsetMs) {
2427     return elapsedRealtimeEpochOffsetMs == C.TIME_UNSET
2428         ? System.currentTimeMillis()
2429         : SystemClock.elapsedRealtime() + elapsedRealtimeEpochOffsetMs;
2430   }
2431 
2432   /**
2433    * Moves the elements starting at {@code fromIndex} to {@code newFromIndex}.
2434    *
2435    * @param items The list of which to move elements.
2436    * @param fromIndex The index at which the items to move start.
2437    * @param toIndex The index up to which elements should be moved (exclusive).
2438    * @param newFromIndex The new from index.
2439    */
moveItems( List<T> items, int fromIndex, int toIndex, int newFromIndex)2440   public static <T extends Object> void moveItems(
2441       List<T> items, int fromIndex, int toIndex, int newFromIndex) {
2442     ArrayDeque<T> removedItems = new ArrayDeque<>();
2443     int removedItemsLength = toIndex - fromIndex;
2444     for (int i = removedItemsLength - 1; i >= 0; i--) {
2445       removedItems.addFirst(items.remove(fromIndex + i));
2446     }
2447     items.addAll(min(newFromIndex, items.size()), removedItems);
2448   }
2449 
2450   /** Returns whether the table exists in the database. */
tableExists(SQLiteDatabase database, String tableName)2451   public static boolean tableExists(SQLiteDatabase database, String tableName) {
2452     long count =
2453         DatabaseUtils.queryNumEntries(
2454             database, "sqlite_master", "tbl_name = ?", new String[] {tableName});
2455     return count > 0;
2456   }
2457 
2458   /**
2459    * Attempts to parse an error code from a diagnostic string found in framework media exceptions.
2460    *
2461    * <p>For example: android.media.MediaCodec.error_1 or android.media.MediaDrm.error_neg_2.
2462    *
2463    * @param diagnosticsInfo A string from which to parse the error code.
2464    * @return The parser error code, or 0 if an error code could not be parsed.
2465    */
getErrorCodeFromPlatformDiagnosticsInfo(@ullable String diagnosticsInfo)2466   public static int getErrorCodeFromPlatformDiagnosticsInfo(@Nullable String diagnosticsInfo) {
2467     // TODO (internal b/192337376): Change 0 for ERROR_UNKNOWN once available.
2468     if (diagnosticsInfo == null) {
2469       return 0;
2470     }
2471     String[] strings = split(diagnosticsInfo, "_");
2472     int length = strings.length;
2473     if (length < 2) {
2474       return 0;
2475     }
2476     String digitsSection = strings[length - 1];
2477     boolean isNegative = length >= 3 && "neg".equals(strings[length - 2]);
2478     try {
2479       int errorCode = Integer.parseInt(Assertions.checkNotNull(digitsSection));
2480       return isNegative ? -errorCode : errorCode;
2481     } catch (NumberFormatException e) {
2482       return 0;
2483     }
2484   }
2485 
2486   /**
2487    * Returns string representation of a {@link C.FormatSupport} flag.
2488    *
2489    * @param formatSupport A {@link C.FormatSupport} flag.
2490    * @return A string representation of the flag.
2491    */
getFormatSupportString(@.FormatSupport int formatSupport)2492   public static String getFormatSupportString(@C.FormatSupport int formatSupport) {
2493     switch (formatSupport) {
2494       case C.FORMAT_HANDLED:
2495         return "YES";
2496       case C.FORMAT_EXCEEDS_CAPABILITIES:
2497         return "NO_EXCEEDS_CAPABILITIES";
2498       case C.FORMAT_UNSUPPORTED_DRM:
2499         return "NO_UNSUPPORTED_DRM";
2500       case C.FORMAT_UNSUPPORTED_SUBTYPE:
2501         return "NO_UNSUPPORTED_TYPE";
2502       case C.FORMAT_UNSUPPORTED_TYPE:
2503         return "NO";
2504       default:
2505         throw new IllegalStateException();
2506     }
2507   }
2508 
2509   /**
2510    * Returns the {@link Commands} available in the {@link Player}.
2511    *
2512    * @param player The {@link Player}.
2513    * @param permanentAvailableCommands The commands permanently available in the player.
2514    * @return The available {@link Commands}.
2515    */
getAvailableCommands(Player player, Commands permanentAvailableCommands)2516   public static Commands getAvailableCommands(Player player, Commands permanentAvailableCommands) {
2517     boolean isPlayingAd = player.isPlayingAd();
2518     boolean isCurrentMediaItemSeekable = player.isCurrentMediaItemSeekable();
2519     boolean hasPreviousMediaItem = player.hasPreviousMediaItem();
2520     boolean hasNextMediaItem = player.hasNextMediaItem();
2521     boolean isCurrentMediaItemLive = player.isCurrentMediaItemLive();
2522     boolean isCurrentMediaItemDynamic = player.isCurrentMediaItemDynamic();
2523     boolean isTimelineEmpty = player.getCurrentTimeline().isEmpty();
2524     return new Commands.Builder()
2525         .addAll(permanentAvailableCommands)
2526         .addIf(COMMAND_SEEK_TO_DEFAULT_POSITION, !isPlayingAd)
2527         .addIf(COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM, isCurrentMediaItemSeekable && !isPlayingAd)
2528         .addIf(COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM, hasPreviousMediaItem && !isPlayingAd)
2529         .addIf(
2530             COMMAND_SEEK_TO_PREVIOUS,
2531             !isTimelineEmpty
2532                 && (hasPreviousMediaItem || !isCurrentMediaItemLive || isCurrentMediaItemSeekable)
2533                 && !isPlayingAd)
2534         .addIf(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM, hasNextMediaItem && !isPlayingAd)
2535         .addIf(
2536             COMMAND_SEEK_TO_NEXT,
2537             !isTimelineEmpty
2538                 && (hasNextMediaItem || (isCurrentMediaItemLive && isCurrentMediaItemDynamic))
2539                 && !isPlayingAd)
2540         .addIf(COMMAND_SEEK_TO_MEDIA_ITEM, !isPlayingAd)
2541         .addIf(COMMAND_SEEK_BACK, isCurrentMediaItemSeekable && !isPlayingAd)
2542         .addIf(COMMAND_SEEK_FORWARD, isCurrentMediaItemSeekable && !isPlayingAd)
2543         .build();
2544   }
2545 
2546   /**
2547    * Returns the sum of all summands of the given array.
2548    *
2549    * @param summands The summands to calculate the sum from.
2550    * @return The sum of all summands.
2551    */
sum(long... summands)2552   public static long sum(long... summands) {
2553     long sum = 0;
2554     for (long summand : summands) {
2555       sum += summand;
2556     }
2557     return sum;
2558   }
2559 
2560   @Nullable
getSystemProperty(String name)2561   private static String getSystemProperty(String name) {
2562     try {
2563       @SuppressLint("PrivateApi")
2564       Class<?> systemProperties = Class.forName("android.os.SystemProperties");
2565       Method getMethod = systemProperties.getMethod("get", String.class);
2566       return (String) getMethod.invoke(systemProperties, name);
2567     } catch (Exception e) {
2568       Log.e(TAG, "Failed to read system property " + name, e);
2569       return null;
2570     }
2571   }
2572 
2573   @RequiresApi(23)
getDisplaySizeV23(Display display, Point outSize)2574   private static void getDisplaySizeV23(Display display, Point outSize) {
2575     Display.Mode mode = display.getMode();
2576     outSize.x = mode.getPhysicalWidth();
2577     outSize.y = mode.getPhysicalHeight();
2578   }
2579 
2580   @RequiresApi(17)
getDisplaySizeV17(Display display, Point outSize)2581   private static void getDisplaySizeV17(Display display, Point outSize) {
2582     display.getRealSize(outSize);
2583   }
2584 
getDisplaySizeV16(Display display, Point outSize)2585   private static void getDisplaySizeV16(Display display, Point outSize) {
2586     display.getSize(outSize);
2587   }
2588 
getSystemLocales()2589   private static String[] getSystemLocales() {
2590     Configuration config = Resources.getSystem().getConfiguration();
2591     return SDK_INT >= 24
2592         ? getSystemLocalesV24(config)
2593         : new String[] {getLocaleLanguageTag(config.locale)};
2594   }
2595 
2596   @RequiresApi(24)
getSystemLocalesV24(Configuration config)2597   private static String[] getSystemLocalesV24(Configuration config) {
2598     return Util.split(config.getLocales().toLanguageTags(), ",");
2599   }
2600 
2601   @RequiresApi(21)
getLocaleLanguageTagV21(Locale locale)2602   private static String getLocaleLanguageTagV21(Locale locale) {
2603     return locale.toLanguageTag();
2604   }
2605 
createIsoLanguageReplacementMap()2606   private static HashMap<String, String> createIsoLanguageReplacementMap() {
2607     String[] iso2Languages = Locale.getISOLanguages();
2608     HashMap<String, String> replacedLanguages =
2609         new HashMap<>(
2610             /* initialCapacity= */ iso2Languages.length + additionalIsoLanguageReplacements.length);
2611     for (String iso2 : iso2Languages) {
2612       try {
2613         // This returns the ISO 639-2/T code for the language.
2614         String iso3 = new Locale(iso2).getISO3Language();
2615         if (!TextUtils.isEmpty(iso3)) {
2616           replacedLanguages.put(iso3, iso2);
2617         }
2618       } catch (MissingResourceException e) {
2619         // Shouldn't happen for list of known languages, but we don't want to throw either.
2620       }
2621     }
2622     // Add additional replacement mappings.
2623     for (int i = 0; i < additionalIsoLanguageReplacements.length; i += 2) {
2624       replacedLanguages.put(
2625           additionalIsoLanguageReplacements[i], additionalIsoLanguageReplacements[i + 1]);
2626     }
2627     return replacedLanguages;
2628   }
2629 
2630   @RequiresApi(api = Build.VERSION_CODES.M)
requestExternalStoragePermission(Activity activity)2631   private static boolean requestExternalStoragePermission(Activity activity) {
2632     if (activity.checkSelfPermission(permission.READ_EXTERNAL_STORAGE)
2633         != PackageManager.PERMISSION_GRANTED) {
2634       activity.requestPermissions(
2635           new String[] {permission.READ_EXTERNAL_STORAGE}, /* requestCode= */ 0);
2636       return true;
2637     }
2638     return false;
2639   }
2640 
2641   @RequiresApi(api = Build.VERSION_CODES.N)
isTrafficRestricted(Uri uri)2642   private static boolean isTrafficRestricted(Uri uri) {
2643     return "http".equals(uri.getScheme())
2644         && !NetworkSecurityPolicy.getInstance()
2645             .isCleartextTrafficPermitted(checkNotNull(uri.getHost()));
2646   }
2647 
maybeReplaceLegacyLanguageTags(String languageTag)2648   private static String maybeReplaceLegacyLanguageTags(String languageTag) {
2649     for (int i = 0; i < isoLegacyTagReplacements.length; i += 2) {
2650       if (languageTag.startsWith(isoLegacyTagReplacements[i])) {
2651         return isoLegacyTagReplacements[i + 1]
2652             + languageTag.substring(/* beginIndex= */ isoLegacyTagReplacements[i].length());
2653       }
2654     }
2655     return languageTag;
2656   }
2657 
2658   // Additional mapping from ISO3 to ISO2 language codes.
2659   private static final String[] additionalIsoLanguageReplacements =
2660       new String[] {
2661         // Bibliographical codes defined in ISO 639-2/B, replaced by terminological code defined in
2662         // ISO 639-2/T. See https://en.wikipedia.org/wiki/List_of_ISO_639-2_codes.
2663         "alb", "sq",
2664         "arm", "hy",
2665         "baq", "eu",
2666         "bur", "my",
2667         "tib", "bo",
2668         "chi", "zh",
2669         "cze", "cs",
2670         "dut", "nl",
2671         "ger", "de",
2672         "gre", "el",
2673         "fre", "fr",
2674         "geo", "ka",
2675         "ice", "is",
2676         "mac", "mk",
2677         "mao", "mi",
2678         "may", "ms",
2679         "per", "fa",
2680         "rum", "ro",
2681         "scc", "hbs-srp",
2682         "slo", "sk",
2683         "wel", "cy",
2684         // Deprecated 2-letter codes, replaced by modern equivalent (including macrolanguage)
2685         // See https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes, "ISO 639:1988"
2686         "id", "ms-ind",
2687         "iw", "he",
2688         "heb", "he",
2689         "ji", "yi",
2690         // Individual macrolanguage codes mapped back to full macrolanguage code.
2691         // See https://en.wikipedia.org/wiki/ISO_639_macrolanguage
2692         "in", "ms-ind",
2693         "ind", "ms-ind",
2694         "nb", "no-nob",
2695         "nob", "no-nob",
2696         "nn", "no-nno",
2697         "nno", "no-nno",
2698         "tw", "ak-twi",
2699         "twi", "ak-twi",
2700         "bs", "hbs-bos",
2701         "bos", "hbs-bos",
2702         "hr", "hbs-hrv",
2703         "hrv", "hbs-hrv",
2704         "sr", "hbs-srp",
2705         "srp", "hbs-srp",
2706         "cmn", "zh-cmn",
2707         "hak", "zh-hak",
2708         "nan", "zh-nan",
2709         "hsn", "zh-hsn"
2710       };
2711 
2712   // Legacy tags that have been replaced by modern equivalents (including macrolanguage)
2713   // See https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry.
2714   private static final String[] isoLegacyTagReplacements =
2715       new String[] {
2716         "i-lux", "lb",
2717         "i-hak", "zh-hak",
2718         "i-navajo", "nv",
2719         "no-bok", "no-nob",
2720         "no-nyn", "no-nno",
2721         "zh-guoyu", "zh-cmn",
2722         "zh-hakka", "zh-hak",
2723         "zh-min-nan", "zh-nan",
2724         "zh-xiang", "zh-hsn"
2725       };
2726 
2727   /**
2728    * Allows the CRC-32 calculation to be done byte by byte instead of bit per bit in the order "most
2729    * significant bit first".
2730    */
2731   private static final int[] CRC32_BYTES_MSBF = {
2732     0X00000000, 0X04C11DB7, 0X09823B6E, 0X0D4326D9, 0X130476DC, 0X17C56B6B, 0X1A864DB2,
2733     0X1E475005, 0X2608EDB8, 0X22C9F00F, 0X2F8AD6D6, 0X2B4BCB61, 0X350C9B64, 0X31CD86D3,
2734     0X3C8EA00A, 0X384FBDBD, 0X4C11DB70, 0X48D0C6C7, 0X4593E01E, 0X4152FDA9, 0X5F15ADAC,
2735     0X5BD4B01B, 0X569796C2, 0X52568B75, 0X6A1936C8, 0X6ED82B7F, 0X639B0DA6, 0X675A1011,
2736     0X791D4014, 0X7DDC5DA3, 0X709F7B7A, 0X745E66CD, 0X9823B6E0, 0X9CE2AB57, 0X91A18D8E,
2737     0X95609039, 0X8B27C03C, 0X8FE6DD8B, 0X82A5FB52, 0X8664E6E5, 0XBE2B5B58, 0XBAEA46EF,
2738     0XB7A96036, 0XB3687D81, 0XAD2F2D84, 0XA9EE3033, 0XA4AD16EA, 0XA06C0B5D, 0XD4326D90,
2739     0XD0F37027, 0XDDB056FE, 0XD9714B49, 0XC7361B4C, 0XC3F706FB, 0XCEB42022, 0XCA753D95,
2740     0XF23A8028, 0XF6FB9D9F, 0XFBB8BB46, 0XFF79A6F1, 0XE13EF6F4, 0XE5FFEB43, 0XE8BCCD9A,
2741     0XEC7DD02D, 0X34867077, 0X30476DC0, 0X3D044B19, 0X39C556AE, 0X278206AB, 0X23431B1C,
2742     0X2E003DC5, 0X2AC12072, 0X128E9DCF, 0X164F8078, 0X1B0CA6A1, 0X1FCDBB16, 0X018AEB13,
2743     0X054BF6A4, 0X0808D07D, 0X0CC9CDCA, 0X7897AB07, 0X7C56B6B0, 0X71159069, 0X75D48DDE,
2744     0X6B93DDDB, 0X6F52C06C, 0X6211E6B5, 0X66D0FB02, 0X5E9F46BF, 0X5A5E5B08, 0X571D7DD1,
2745     0X53DC6066, 0X4D9B3063, 0X495A2DD4, 0X44190B0D, 0X40D816BA, 0XACA5C697, 0XA864DB20,
2746     0XA527FDF9, 0XA1E6E04E, 0XBFA1B04B, 0XBB60ADFC, 0XB6238B25, 0XB2E29692, 0X8AAD2B2F,
2747     0X8E6C3698, 0X832F1041, 0X87EE0DF6, 0X99A95DF3, 0X9D684044, 0X902B669D, 0X94EA7B2A,
2748     0XE0B41DE7, 0XE4750050, 0XE9362689, 0XEDF73B3E, 0XF3B06B3B, 0XF771768C, 0XFA325055,
2749     0XFEF34DE2, 0XC6BCF05F, 0XC27DEDE8, 0XCF3ECB31, 0XCBFFD686, 0XD5B88683, 0XD1799B34,
2750     0XDC3ABDED, 0XD8FBA05A, 0X690CE0EE, 0X6DCDFD59, 0X608EDB80, 0X644FC637, 0X7A089632,
2751     0X7EC98B85, 0X738AAD5C, 0X774BB0EB, 0X4F040D56, 0X4BC510E1, 0X46863638, 0X42472B8F,
2752     0X5C007B8A, 0X58C1663D, 0X558240E4, 0X51435D53, 0X251D3B9E, 0X21DC2629, 0X2C9F00F0,
2753     0X285E1D47, 0X36194D42, 0X32D850F5, 0X3F9B762C, 0X3B5A6B9B, 0X0315D626, 0X07D4CB91,
2754     0X0A97ED48, 0X0E56F0FF, 0X1011A0FA, 0X14D0BD4D, 0X19939B94, 0X1D528623, 0XF12F560E,
2755     0XF5EE4BB9, 0XF8AD6D60, 0XFC6C70D7, 0XE22B20D2, 0XE6EA3D65, 0XEBA91BBC, 0XEF68060B,
2756     0XD727BBB6, 0XD3E6A601, 0XDEA580D8, 0XDA649D6F, 0XC423CD6A, 0XC0E2D0DD, 0XCDA1F604,
2757     0XC960EBB3, 0XBD3E8D7E, 0XB9FF90C9, 0XB4BCB610, 0XB07DABA7, 0XAE3AFBA2, 0XAAFBE615,
2758     0XA7B8C0CC, 0XA379DD7B, 0X9B3660C6, 0X9FF77D71, 0X92B45BA8, 0X9675461F, 0X8832161A,
2759     0X8CF30BAD, 0X81B02D74, 0X857130C3, 0X5D8A9099, 0X594B8D2E, 0X5408ABF7, 0X50C9B640,
2760     0X4E8EE645, 0X4A4FFBF2, 0X470CDD2B, 0X43CDC09C, 0X7B827D21, 0X7F436096, 0X7200464F,
2761     0X76C15BF8, 0X68860BFD, 0X6C47164A, 0X61043093, 0X65C52D24, 0X119B4BE9, 0X155A565E,
2762     0X18197087, 0X1CD86D30, 0X029F3D35, 0X065E2082, 0X0B1D065B, 0X0FDC1BEC, 0X3793A651,
2763     0X3352BBE6, 0X3E119D3F, 0X3AD08088, 0X2497D08D, 0X2056CD3A, 0X2D15EBE3, 0X29D4F654,
2764     0XC5A92679, 0XC1683BCE, 0XCC2B1D17, 0XC8EA00A0, 0XD6AD50A5, 0XD26C4D12, 0XDF2F6BCB,
2765     0XDBEE767C, 0XE3A1CBC1, 0XE760D676, 0XEA23F0AF, 0XEEE2ED18, 0XF0A5BD1D, 0XF464A0AA,
2766     0XF9278673, 0XFDE69BC4, 0X89B8FD09, 0X8D79E0BE, 0X803AC667, 0X84FBDBD0, 0X9ABC8BD5,
2767     0X9E7D9662, 0X933EB0BB, 0X97FFAD0C, 0XAFB010B1, 0XAB710D06, 0XA6322BDF, 0XA2F33668,
2768     0XBCB4666D, 0XB8757BDA, 0XB5365D03, 0XB1F740B4
2769   };
2770 
2771   /**
2772    * Allows the CRC-8 calculation to be done byte by byte instead of bit per bit in the order "most
2773    * significant bit first".
2774    */
2775   private static final int[] CRC8_BYTES_MSBF = {
2776     0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A,
2777     0x2D, 0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53,
2778     0x5A, 0x5D, 0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5, 0xD8, 0xDF, 0xD6, 0xD1, 0xC4,
2779     0xC3, 0xCA, 0xCD, 0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, 0xA8, 0xAF, 0xA6, 0xA1,
2780     0xB4, 0xB3, 0xBA, 0xBD, 0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2, 0xFF, 0xF8, 0xF1,
2781     0xF6, 0xE3, 0xE4, 0xED, 0xEA, 0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2, 0x8F, 0x88,
2782     0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A, 0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, 0x1F,
2783     0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A, 0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42,
2784     0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A, 0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B,
2785     0x9C, 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4, 0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2,
2786     0xEB, 0xEC, 0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4, 0x69, 0x6E, 0x67, 0x60, 0x75,
2787     0x72, 0x7B, 0x7C, 0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44, 0x19, 0x1E, 0x17, 0x10,
2788     0x05, 0x02, 0x0B, 0x0C, 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34, 0x4E, 0x49, 0x40,
2789     0x47, 0x52, 0x55, 0x5C, 0x5B, 0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63, 0x3E, 0x39,
2790     0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B, 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13, 0xAE,
2791     0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83,
2792     0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4,
2793     0xF3
2794   };
2795 }
2796