• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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&lt;String, Object&gt;} 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