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} < 0, {@code toIndex} > {@code 278 * list.size()}, or {@code fromIndex} > {@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 ≥ 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 < right, or a positive value if left 939 * > 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 (> 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 * <, >, :, ", /, \, |, ?, 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