1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.media; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.annotation.TestApi; 22 import android.content.ContentResolver; 23 import android.content.Context; 24 import android.database.Cursor; 25 import android.net.Uri; 26 import android.os.Binder; 27 import android.os.Environment; 28 import android.os.FileUtils; 29 import android.os.Handler; 30 import android.provider.OpenableColumns; 31 import android.util.Log; 32 import android.util.Pair; 33 import android.util.Range; 34 import android.util.Rational; 35 import android.util.Size; 36 37 import com.android.internal.annotations.GuardedBy; 38 39 import java.io.File; 40 import java.io.FileNotFoundException; 41 import java.util.Arrays; 42 import java.util.Comparator; 43 import java.util.HashMap; 44 import java.util.Objects; 45 import java.util.Vector; 46 import java.util.concurrent.Executor; 47 48 /** 49 * Media Utilities 50 * 51 * This class is hidden but public to allow CTS testing and verification 52 * of the static methods and classes. 53 * 54 * @hide 55 */ 56 public class Utils { 57 private static final String TAG = "Utils"; 58 59 /** 60 * Sorts distinct (non-intersecting) range array in ascending order. 61 * @throws java.lang.IllegalArgumentException if ranges are not distinct 62 */ sortDistinctRanges(Range<T>[] ranges)63 public static <T extends Comparable<? super T>> void sortDistinctRanges(Range<T>[] ranges) { 64 Arrays.sort(ranges, new Comparator<Range<T>>() { 65 @Override 66 public int compare(Range<T> lhs, Range<T> rhs) { 67 if (lhs.getUpper().compareTo(rhs.getLower()) < 0) { 68 return -1; 69 } else if (lhs.getLower().compareTo(rhs.getUpper()) > 0) { 70 return 1; 71 } 72 throw new IllegalArgumentException( 73 "sample rate ranges must be distinct (" + lhs + " and " + rhs + ")"); 74 } 75 }); 76 } 77 78 /** 79 * Returns the intersection of two sets of non-intersecting ranges 80 * @param one a sorted set of non-intersecting ranges in ascending order 81 * @param another another sorted set of non-intersecting ranges in ascending order 82 * @return the intersection of the two sets, sorted in ascending order 83 */ 84 public static <T extends Comparable<? super T>> intersectSortedDistinctRanges(Range<T>[] one, Range<T>[] another)85 Range<T>[] intersectSortedDistinctRanges(Range<T>[] one, Range<T>[] another) { 86 int ix = 0; 87 Vector<Range<T>> result = new Vector<Range<T>>(); 88 for (Range<T> range: another) { 89 while (ix < one.length && 90 one[ix].getUpper().compareTo(range.getLower()) < 0) { 91 ++ix; 92 } 93 while (ix < one.length && 94 one[ix].getUpper().compareTo(range.getUpper()) < 0) { 95 result.add(range.intersect(one[ix])); 96 ++ix; 97 } 98 if (ix == one.length) { 99 break; 100 } 101 if (one[ix].getLower().compareTo(range.getUpper()) <= 0) { 102 result.add(range.intersect(one[ix])); 103 } 104 } 105 return result.toArray(new Range[result.size()]); 106 } 107 108 /** 109 * Returns the index of the range that contains a value in a sorted array of distinct ranges. 110 * @param ranges a sorted array of non-intersecting ranges in ascending order 111 * @param value the value to search for 112 * @return if the value is in one of the ranges, it returns the index of that range. Otherwise, 113 * the return value is {@code (-1-index)} for the {@code index} of the range that is 114 * immediately following {@code value}. 115 */ 116 public static <T extends Comparable<? super T>> binarySearchDistinctRanges(Range<T>[] ranges, T value)117 int binarySearchDistinctRanges(Range<T>[] ranges, T value) { 118 return Arrays.binarySearch(ranges, Range.create(value, value), 119 new Comparator<Range<T>>() { 120 @Override 121 public int compare(Range<T> lhs, Range<T> rhs) { 122 if (lhs.getUpper().compareTo(rhs.getLower()) < 0) { 123 return -1; 124 } else if (lhs.getLower().compareTo(rhs.getUpper()) > 0) { 125 return 1; 126 } 127 return 0; 128 } 129 }); 130 } 131 132 /** 133 * Returns greatest common divisor 134 */ 135 static int gcd(int a, int b) { 136 if (a == 0 && b == 0) { 137 return 1; 138 } 139 if (b < 0) { 140 b = -b; 141 } 142 if (a < 0) { 143 a = -a; 144 } 145 while (a != 0) { 146 int c = b % a; 147 b = a; 148 a = c; 149 } 150 return b; 151 } 152 153 /** Returns the equivalent factored range {@code newrange}, where for every 154 * {@code e}: {@code newrange.contains(e)} implies that {@code range.contains(e * factor)}, 155 * and {@code !newrange.contains(e)} implies that {@code !range.contains(e * factor)}. 156 */ 157 static Range<Integer>factorRange(Range<Integer> range, int factor) { 158 if (factor == 1) { 159 return range; 160 } 161 return Range.create(divUp(range.getLower(), factor), range.getUpper() / factor); 162 } 163 164 /** Returns the equivalent factored range {@code newrange}, where for every 165 * {@code e}: {@code newrange.contains(e)} implies that {@code range.contains(e * factor)}, 166 * and {@code !newrange.contains(e)} implies that {@code !range.contains(e * factor)}. 167 */ 168 static Range<Long>factorRange(Range<Long> range, long factor) { 169 if (factor == 1) { 170 return range; 171 } 172 return Range.create(divUp(range.getLower(), factor), range.getUpper() / factor); 173 } 174 175 private static Rational scaleRatio(Rational ratio, int num, int den) { 176 int common = gcd(num, den); 177 num /= common; 178 den /= common; 179 return new Rational( 180 (int)(ratio.getNumerator() * (double)num), // saturate to int 181 (int)(ratio.getDenominator() * (double)den)); // saturate to int 182 } 183 184 static Range<Rational> scaleRange(Range<Rational> range, int num, int den) { 185 if (num == den) { 186 return range; 187 } 188 return Range.create( 189 scaleRatio(range.getLower(), num, den), 190 scaleRatio(range.getUpper(), num, den)); 191 } 192 193 static Range<Integer> alignRange(Range<Integer> range, int align) { 194 return range.intersect( 195 divUp(range.getLower(), align) * align, 196 (range.getUpper() / align) * align); 197 } 198 199 static int divUp(int num, int den) { 200 return (num + den - 1) / den; 201 } 202 203 static long divUp(long num, long den) { 204 return (num + den - 1) / den; 205 } 206 207 /** 208 * Returns least common multiple 209 */ 210 private static long lcm(int a, int b) { 211 if (a == 0 || b == 0) { 212 throw new IllegalArgumentException("lce is not defined for zero arguments"); 213 } 214 return (long)a * b / gcd(a, b); 215 } 216 217 static Range<Integer> intRangeFor(double v) { 218 return Range.create((int)v, (int)Math.ceil(v)); 219 } 220 221 static Range<Long> longRangeFor(double v) { 222 return Range.create((long)v, (long)Math.ceil(v)); 223 } 224 225 static Size parseSize(Object o, Size fallback) { 226 if (o == null) { 227 return fallback; 228 } 229 try { 230 return Size.parseSize((String) o); 231 } catch (ClassCastException e) { 232 } catch (NumberFormatException e) { 233 } 234 Log.w(TAG, "could not parse size '" + o + "'"); 235 return fallback; 236 } 237 238 static int parseIntSafely(Object o, int fallback) { 239 if (o == null) { 240 return fallback; 241 } 242 try { 243 String s = (String)o; 244 return Integer.parseInt(s); 245 } catch (ClassCastException e) { 246 } catch (NumberFormatException e) { 247 } 248 Log.w(TAG, "could not parse integer '" + o + "'"); 249 return fallback; 250 } 251 252 static Range<Integer> parseIntRange(Object o, Range<Integer> fallback) { 253 if (o == null) { 254 return fallback; 255 } 256 try { 257 String s = (String)o; 258 int ix = s.indexOf('-'); 259 if (ix >= 0) { 260 return Range.create( 261 Integer.parseInt(s.substring(0, ix), 10), 262 Integer.parseInt(s.substring(ix + 1), 10)); 263 } 264 int value = Integer.parseInt(s); 265 return Range.create(value, value); 266 } catch (ClassCastException e) { 267 } catch (NumberFormatException e) { 268 } catch (IllegalArgumentException e) { 269 } 270 Log.w(TAG, "could not parse integer range '" + o + "'"); 271 return fallback; 272 } 273 274 static Range<Long> parseLongRange(Object o, Range<Long> fallback) { 275 if (o == null) { 276 return fallback; 277 } 278 try { 279 String s = (String)o; 280 int ix = s.indexOf('-'); 281 if (ix >= 0) { 282 return Range.create( 283 Long.parseLong(s.substring(0, ix), 10), 284 Long.parseLong(s.substring(ix + 1), 10)); 285 } 286 long value = Long.parseLong(s); 287 return Range.create(value, value); 288 } catch (ClassCastException e) { 289 } catch (NumberFormatException e) { 290 } catch (IllegalArgumentException e) { 291 } 292 Log.w(TAG, "could not parse long range '" + o + "'"); 293 return fallback; 294 } 295 296 static Range<Rational> parseRationalRange(Object o, Range<Rational> fallback) { 297 if (o == null) { 298 return fallback; 299 } 300 try { 301 String s = (String)o; 302 int ix = s.indexOf('-'); 303 if (ix >= 0) { 304 return Range.create( 305 Rational.parseRational(s.substring(0, ix)), 306 Rational.parseRational(s.substring(ix + 1))); 307 } 308 Rational value = Rational.parseRational(s); 309 return Range.create(value, value); 310 } catch (ClassCastException e) { 311 } catch (NumberFormatException e) { 312 } catch (IllegalArgumentException e) { 313 } 314 Log.w(TAG, "could not parse rational range '" + o + "'"); 315 return fallback; 316 } 317 318 static Pair<Size, Size> parseSizeRange(Object o) { 319 if (o == null) { 320 return null; 321 } 322 try { 323 String s = (String)o; 324 int ix = s.indexOf('-'); 325 if (ix >= 0) { 326 return Pair.create( 327 Size.parseSize(s.substring(0, ix)), 328 Size.parseSize(s.substring(ix + 1))); 329 } 330 Size value = Size.parseSize(s); 331 return Pair.create(value, value); 332 } catch (ClassCastException e) { 333 } catch (NumberFormatException e) { 334 } catch (IllegalArgumentException e) { 335 } 336 Log.w(TAG, "could not parse size range '" + o + "'"); 337 return null; 338 } 339 340 /** 341 * Creates a unique file in the specified external storage with the desired name. If the name is 342 * taken, the new file's name will have '(%d)' to avoid overwriting files. 343 * 344 * @param context {@link Context} to query the file name from. 345 * @param subdirectory One of the directories specified in {@link android.os.Environment} 346 * @param fileName desired name for the file. 347 * @param mimeType MIME type of the file to create. 348 * @return the File object in the storage, or null if an error occurs. 349 */ 350 public static File getUniqueExternalFile(Context context, String subdirectory, String fileName, 351 String mimeType) { 352 File externalStorage = Environment.getExternalStoragePublicDirectory(subdirectory); 353 // Make sure the storage subdirectory exists 354 externalStorage.mkdirs(); 355 356 File outFile = null; 357 try { 358 // Ensure the file has a unique name, as to not override any existing file 359 outFile = FileUtils.buildUniqueFile(externalStorage, mimeType, fileName); 360 } catch (FileNotFoundException e) { 361 // This might also be reached if the number of repeated files gets too high 362 Log.e(TAG, "Unable to get a unique file name: " + e); 363 return null; 364 } 365 return outFile; 366 } 367 368 /** 369 * Returns a file's display name from its {@link android.content.ContentResolver.SCHEME_FILE} 370 * or {@link android.content.ContentResolver.SCHEME_CONTENT} Uri. The display name of a file 371 * includes its extension. 372 * 373 * @param context Context trying to resolve the file's display name. 374 * @param uri Uri of the file. 375 * @return the file's display name, or the uri's string if something fails or the uri isn't in 376 * the schemes specified above. 377 */ 378 static String getFileDisplayNameFromUri(Context context, Uri uri) { 379 String scheme = uri.getScheme(); 380 381 if (ContentResolver.SCHEME_FILE.equals(scheme)) { 382 return uri.getLastPathSegment(); 383 } else if (ContentResolver.SCHEME_CONTENT.equals(scheme)) { 384 // We need to query the ContentResolver to get the actual file name as the Uri masks it. 385 // This means we want the name used for display purposes only. 386 String[] proj = { 387 OpenableColumns.DISPLAY_NAME 388 }; 389 try (Cursor cursor = context.getContentResolver().query(uri, proj, null, null, null)) { 390 if (cursor != null && cursor.getCount() != 0) { 391 cursor.moveToFirst(); 392 return cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)); 393 } 394 } 395 } 396 397 // This will only happen if the Uri isn't either SCHEME_CONTENT or SCHEME_FILE, so we assume 398 // it already represents the file's name. 399 return uri.toString(); 400 } 401 402 /** 403 * {@code ListenerList} is a helper class that delivers events to listeners. 404 * 405 * It is written to isolate the <strong>mechanics</strong> of event delivery from the 406 * <strong>details</strong> of those events. 407 * 408 * The {@code ListenerList} is parameterized on the generic type {@code V} 409 * of the object delivered by {@code notify()}. 410 * This gives compile time type safety over run-time casting of a general {@code Object}, 411 * much like {@code HashMap<String, Object>} does not give type safety of the 412 * stored {@code Object} value and may allow 413 * permissive storage of {@code Object}s that are not expected by users of the 414 * {@code HashMap}, later resulting in run-time cast exceptions that 415 * could have been caught by replacing 416 * {@code Object} with a more precise type to enforce a compile time contract. 417 * 418 * The {@code ListenerList} is implemented as a single method callback 419 * - or a "listener" according to Android style guidelines. 420 * 421 * The {@code ListenerList} can be trivially extended by a suitable lambda to implement 422 * a <strong> multiple method abstract class</strong> "callback", 423 * in which the generic type {@code V} could be an {@code Object} 424 * to encapsulate the details of the parameters of each callback method, and 425 * {@code instanceof} could be used to disambiguate which callback method to use. 426 * A {@link Bundle} could alternatively encapsulate those generic parameters, 427 * perhaps more conveniently. 428 * Again, this is a detail of the event, not the mechanics of the event delivery, 429 * which this class is concerned with. 430 * 431 * For details on how to use this class to implement a <strong>single listener</strong> 432 * {@code ListenerList}, see notes on {@link #add}. 433 * 434 * For details on how to optimize this class to implement 435 * a listener based on {@link Handler}s 436 * instead of {@link Executor}s, see{@link #ListenerList(boolean, boolean, boolean)}. 437 * 438 * This is a TestApi for CTS Unit Testing, not exposed for general Application use. 439 * @hide 440 * 441 * @param <V> The class of the object returned to the listener. 442 */ 443 @TestApi 444 public static class ListenerList<V> { 445 /** 446 * The Listener interface for callback. 447 * 448 * @param <V> The class of the object returned to the listener 449 */ 450 public interface Listener<V> { 451 /** 452 * General event listener interface which is managed by the {@code ListenerList}. 453 * 454 * @param eventCode is an integer representing the event type. This is an 455 * implementation defined parameter. 456 * @param info is the object returned to the listener. It is expected 457 * that the listener makes a private copy of the {@code info} object before 458 * modification, as it is the same instance passed to all listeners. 459 * This is an implementation defined parameter that may be null. 460 */ 461 void onEvent(int eventCode, @Nullable V info); 462 } 463 464 private interface ListenerWithCancellation<V> extends Listener<V> { 465 void cancel(); 466 } 467 468 /** 469 * Default {@code ListenerList} constructor for {@link Executor} based implementation. 470 * 471 * TODO: consider adding a "name" for debugging if this is used for 472 * multiple listener implementations. 473 */ 474 public ListenerList() { 475 this(true /* restrictSingleCallerOnEvent */, 476 true /* clearCallingIdentity */, 477 false /* forceRemoveConsistency*/); 478 } 479 480 /** 481 * Specific {@code ListenerList} constructor for customization. 482 * 483 * See the internal notes for the corresponding private variables on the behavior of 484 * the boolean configuration parameters. 485 * 486 * {@code ListenerList(true, true, false)} is the default and used for 487 * {@link Executor} based notification implementation. 488 * 489 * {@code ListenerList(false, false, false)} may be used for as an optimization 490 * where the {@link Executor} is actually a {@link Handler} post. 491 * 492 * @param restrictSingleCallerOnEvent whether the listener will only be called by 493 * a single thread at a time. 494 * @param clearCallingIdentity whether the binder calling identity on 495 * {@link #notify} is cleared. 496 * @param forceRemoveConsistency whether remove() guarantees no more callbacks to 497 * the listener immediately after the call. 498 */ 499 public ListenerList(boolean restrictSingleCallerOnEvent, 500 boolean clearCallingIdentity, 501 boolean forceRemoveConsistency) { 502 mRestrictSingleCallerOnEvent = restrictSingleCallerOnEvent; 503 mClearCallingIdentity = clearCallingIdentity; 504 mForceRemoveConsistency = forceRemoveConsistency; 505 } 506 507 /** 508 * Adds a listener to the {@code ListenerList}. 509 * 510 * The {@code ListenerList} is most often used to hold {@code multiple} listeners. 511 * 512 * Per Android style, for a single method Listener interface, the add and remove 513 * would be wrapped in "addSomeListener" or "removeSomeListener"; 514 * or a lambda implemented abstract class callback, wrapped in 515 * "registerSomeCallback" or "unregisterSomeCallback". 516 * 517 * We allow a general {@code key} to be attached to add and remove that specific 518 * listener. It could be the {@code listener} object itself. 519 * 520 * For some implementations, there may be only a {@code single} listener permitted. 521 * 522 * Per Android style, for a single listener {@code ListenerList}, 523 * the naming of the wrapping call to {@link #add} would be 524 * "setSomeListener" with a nullable listener, which would be null 525 * to call {@link #remove}. 526 * 527 * In that case, the caller may use this {@link #add} with a single constant object for 528 * the {@code key} to enforce only one Listener in the {@code ListenerList}. 529 * Likewise on remove it would use that 530 * same single constant object to remove the listener. 531 * That {@code key} object could be the {@code ListenerList} itself for convenience. 532 * 533 * @param key is a unique object that is used to identify the listener 534 * when {@code remove()} is called. It can be the listener itself. 535 * @param executor is used to execute the callback. 536 * @param listener is the {@link AudioTrack.ListenerList.Listener} 537 * interface to be called upon {@link notify}. 538 */ 539 public void add( 540 @NonNull Object key, @NonNull Executor executor, @NonNull Listener<V> listener) { 541 Objects.requireNonNull(key); 542 Objects.requireNonNull(executor); 543 Objects.requireNonNull(listener); 544 545 // construct wrapper outside of lock. 546 ListenerWithCancellation<V> listenerWithCancellation = 547 new ListenerWithCancellation<V>() { 548 private final Object mLock = new Object(); // our lock is per Listener. 549 private volatile boolean mCancelled = false; // atomic rmw not needed. 550 551 @Override 552 public void onEvent(int eventCode, V info) { 553 executor.execute(() -> { 554 // Note deep execution of locking and cancellation 555 // so this works after posting on different threads. 556 if (mRestrictSingleCallerOnEvent || mForceRemoveConsistency) { 557 synchronized (mLock) { 558 if (mCancelled) return; 559 listener.onEvent(eventCode, info); 560 } 561 } else { 562 if (mCancelled) return; 563 listener.onEvent(eventCode, info); 564 } 565 }); 566 } 567 568 @Override 569 public void cancel() { 570 if (mForceRemoveConsistency) { 571 synchronized (mLock) { 572 mCancelled = true; 573 } 574 } else { 575 mCancelled = true; 576 } 577 } 578 }; 579 580 synchronized (mListeners) { 581 // TODO: consider an option to check the existence of the key 582 // and throw an ISE if it exists. 583 mListeners.put(key, listenerWithCancellation); // replaces old value 584 } 585 } 586 587 /** 588 * Removes a listener from the {@code ListenerList}. 589 * 590 * @param key the unique object associated with the listener during {@link #add}. 591 */ 592 public void remove(@NonNull Object key) { 593 Objects.requireNonNull(key); 594 595 ListenerWithCancellation<V> listener; 596 synchronized (mListeners) { 597 listener = mListeners.get(key); 598 if (listener == null) { // TODO: consider an option to throw ISE Here. 599 return; 600 } 601 mListeners.remove(key); // removes if exist 602 } 603 604 // cancel outside of lock 605 listener.cancel(); 606 } 607 608 /** 609 * Notifies all listeners on the List. 610 * 611 * @param eventCode to pass to all listeners. 612 * @param info to pass to all listeners. This is an implemention defined parameter 613 * which may be {@code null}. 614 */ 615 public void notify(int eventCode, @Nullable V info) { 616 Object[] listeners; // note we can't cast an object array to a listener array 617 synchronized (mListeners) { 618 if (mListeners.size() == 0) { 619 return; 620 } 621 listeners = mListeners.values().toArray(); // guarantees a copy. 622 } 623 624 // notify outside of lock. 625 final Long identity = mClearCallingIdentity ? Binder.clearCallingIdentity() : null; 626 try { 627 for (Object object : listeners) { 628 final ListenerWithCancellation<V> listener = 629 (ListenerWithCancellation<V>) object; 630 listener.onEvent(eventCode, info); 631 } 632 } finally { 633 if (identity != null) { 634 Binder.restoreCallingIdentity(identity); 635 } 636 } 637 } 638 639 @GuardedBy("mListeners") 640 private HashMap<Object, ListenerWithCancellation<V>> mListeners = new HashMap<>(); 641 642 // An Executor may run in multiple threads, whereas a Handler runs on a single Looper. 643 // Should be true for an Executor to avoid concurrent calling into the same listener, 644 // can be false for a Handler as a Handler forces single thread caller for each listener. 645 private final boolean mRestrictSingleCallerOnEvent; // default true 646 647 // An Executor may run in the calling thread, whereas a handler will post to the Looper. 648 // Should be true for an Executor to prevent privilege escalation, 649 // can be false for a Handler as its thread is not the calling binder thread. 650 private final boolean mClearCallingIdentity; // default true 651 652 // Guaranteeing no listener callbacks after removal requires taking the same lock for the 653 // remove as the callback; this is a reversal in calling layers, 654 // hence the risk of lock order inversion is great. 655 // 656 // Set to true only if you can control the caller's listen and remove methods and/or 657 // the threading of the Executor used for each listener. 658 // When set to false, we do not lock, but still do a best effort to cancel messages 659 // on the fly. 660 private final boolean mForceRemoveConsistency; // default false 661 } 662 } 663