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