1 /*
2  * Copyright (C) 2015 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 android.hardware.radio;
17 
18 import android.annotation.NonNull;
19 import android.annotation.SystemApi;
20 import android.content.ContentResolver;
21 import android.graphics.Bitmap;
22 import android.graphics.BitmapFactory;
23 import android.net.Uri;
24 import android.os.Bundle;
25 import android.os.Parcel;
26 import android.os.Parcelable;
27 import android.text.TextUtils;
28 import android.util.ArrayMap;
29 import android.util.Log;
30 import android.util.SparseArray;
31 
32 import java.util.ArrayList;
33 import java.util.Set;
34 
35 /**
36  * Contains meta data about a radio program such as station name, song title, artist etc...
37  * @hide
38  */
39 @SystemApi
40 public final class RadioMetadata implements Parcelable {
41     private static final String TAG = "RadioMetadata";
42 
43     /**
44      * The RDS Program Information.
45      */
46     public static final String METADATA_KEY_RDS_PI = "android.hardware.radio.metadata.RDS_PI";
47 
48     /**
49      * The RDS Program Service.
50      */
51     public static final String METADATA_KEY_RDS_PS = "android.hardware.radio.metadata.RDS_PS";
52 
53     /**
54      * The RDS PTY.
55      */
56     public static final String METADATA_KEY_RDS_PTY = "android.hardware.radio.metadata.RDS_PTY";
57 
58     /**
59      * The RBDS PTY.
60      */
61     public static final String METADATA_KEY_RBDS_PTY = "android.hardware.radio.metadata.RBDS_PTY";
62 
63     /**
64      * The RBDS Radio Text.
65      */
66     public static final String METADATA_KEY_RDS_RT = "android.hardware.radio.metadata.RDS_RT";
67 
68     /**
69      * The song title.
70      */
71     public static final String METADATA_KEY_TITLE = "android.hardware.radio.metadata.TITLE";
72 
73     /**
74      * The artist name.
75      */
76     public static final String METADATA_KEY_ARTIST = "android.hardware.radio.metadata.ARTIST";
77 
78     /**
79      * The album name.
80      */
81     public static final String METADATA_KEY_ALBUM = "android.hardware.radio.metadata.ALBUM";
82 
83     /**
84      * The music genre.
85      */
86     public static final String METADATA_KEY_GENRE = "android.hardware.radio.metadata.GENRE";
87 
88     /**
89      * The radio station icon {@link Bitmap}.
90      */
91     public static final String METADATA_KEY_ICON = "android.hardware.radio.metadata.ICON";
92 
93     /**
94      * The artwork for the song/album {@link Bitmap}.
95      */
96     public static final String METADATA_KEY_ART = "android.hardware.radio.metadata.ART";
97 
98     /**
99      * The clock.
100      */
101     public static final String METADATA_KEY_CLOCK = "android.hardware.radio.metadata.CLOCK";
102 
103 
104     private static final int METADATA_TYPE_INVALID = -1;
105     private static final int METADATA_TYPE_INT = 0;
106     private static final int METADATA_TYPE_TEXT = 1;
107     private static final int METADATA_TYPE_BITMAP = 2;
108     private static final int METADATA_TYPE_CLOCK = 3;
109 
110     private static final ArrayMap<String, Integer> METADATA_KEYS_TYPE;
111 
112     static {
113         METADATA_KEYS_TYPE = new ArrayMap<String, Integer>();
METADATA_KEYS_TYPE.put(METADATA_KEY_RDS_PI, METADATA_TYPE_TEXT)114         METADATA_KEYS_TYPE.put(METADATA_KEY_RDS_PI, METADATA_TYPE_TEXT);
METADATA_KEYS_TYPE.put(METADATA_KEY_RDS_PS, METADATA_TYPE_TEXT)115         METADATA_KEYS_TYPE.put(METADATA_KEY_RDS_PS, METADATA_TYPE_TEXT);
METADATA_KEYS_TYPE.put(METADATA_KEY_RDS_PTY, METADATA_TYPE_INT)116         METADATA_KEYS_TYPE.put(METADATA_KEY_RDS_PTY, METADATA_TYPE_INT);
METADATA_KEYS_TYPE.put(METADATA_KEY_RBDS_PTY, METADATA_TYPE_INT)117         METADATA_KEYS_TYPE.put(METADATA_KEY_RBDS_PTY, METADATA_TYPE_INT);
METADATA_KEYS_TYPE.put(METADATA_KEY_RDS_RT, METADATA_TYPE_TEXT)118         METADATA_KEYS_TYPE.put(METADATA_KEY_RDS_RT, METADATA_TYPE_TEXT);
METADATA_KEYS_TYPE.put(METADATA_KEY_TITLE, METADATA_TYPE_TEXT)119         METADATA_KEYS_TYPE.put(METADATA_KEY_TITLE, METADATA_TYPE_TEXT);
METADATA_KEYS_TYPE.put(METADATA_KEY_ARTIST, METADATA_TYPE_TEXT)120         METADATA_KEYS_TYPE.put(METADATA_KEY_ARTIST, METADATA_TYPE_TEXT);
METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM, METADATA_TYPE_TEXT)121         METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM, METADATA_TYPE_TEXT);
METADATA_KEYS_TYPE.put(METADATA_KEY_GENRE, METADATA_TYPE_TEXT)122         METADATA_KEYS_TYPE.put(METADATA_KEY_GENRE, METADATA_TYPE_TEXT);
METADATA_KEYS_TYPE.put(METADATA_KEY_ICON, METADATA_TYPE_BITMAP)123         METADATA_KEYS_TYPE.put(METADATA_KEY_ICON, METADATA_TYPE_BITMAP);
METADATA_KEYS_TYPE.put(METADATA_KEY_ART, METADATA_TYPE_BITMAP)124         METADATA_KEYS_TYPE.put(METADATA_KEY_ART, METADATA_TYPE_BITMAP);
METADATA_KEYS_TYPE.put(METADATA_KEY_CLOCK, METADATA_TYPE_CLOCK)125         METADATA_KEYS_TYPE.put(METADATA_KEY_CLOCK, METADATA_TYPE_CLOCK);
126     }
127 
128     // keep in sync with: system/media/radio/include/system/radio_metadata.h
129     private static final int NATIVE_KEY_INVALID     = -1;
130     private static final int NATIVE_KEY_RDS_PI      = 0;
131     private static final int NATIVE_KEY_RDS_PS      = 1;
132     private static final int NATIVE_KEY_RDS_PTY     = 2;
133     private static final int NATIVE_KEY_RBDS_PTY    = 3;
134     private static final int NATIVE_KEY_RDS_RT      = 4;
135     private static final int NATIVE_KEY_TITLE       = 5;
136     private static final int NATIVE_KEY_ARTIST      = 6;
137     private static final int NATIVE_KEY_ALBUM       = 7;
138     private static final int NATIVE_KEY_GENRE       = 8;
139     private static final int NATIVE_KEY_ICON        = 9;
140     private static final int NATIVE_KEY_ART         = 10;
141     private static final int NATIVE_KEY_CLOCK       = 11;
142 
143     private static final SparseArray<String> NATIVE_KEY_MAPPING;
144 
145     static {
146         NATIVE_KEY_MAPPING = new SparseArray<String>();
NATIVE_KEY_MAPPING.put(NATIVE_KEY_RDS_PI, METADATA_KEY_RDS_PI)147         NATIVE_KEY_MAPPING.put(NATIVE_KEY_RDS_PI, METADATA_KEY_RDS_PI);
NATIVE_KEY_MAPPING.put(NATIVE_KEY_RDS_PS, METADATA_KEY_RDS_PS)148         NATIVE_KEY_MAPPING.put(NATIVE_KEY_RDS_PS, METADATA_KEY_RDS_PS);
NATIVE_KEY_MAPPING.put(NATIVE_KEY_RDS_PTY, METADATA_KEY_RDS_PTY)149         NATIVE_KEY_MAPPING.put(NATIVE_KEY_RDS_PTY, METADATA_KEY_RDS_PTY);
NATIVE_KEY_MAPPING.put(NATIVE_KEY_RBDS_PTY, METADATA_KEY_RBDS_PTY)150         NATIVE_KEY_MAPPING.put(NATIVE_KEY_RBDS_PTY, METADATA_KEY_RBDS_PTY);
NATIVE_KEY_MAPPING.put(NATIVE_KEY_RDS_RT, METADATA_KEY_RDS_RT)151         NATIVE_KEY_MAPPING.put(NATIVE_KEY_RDS_RT, METADATA_KEY_RDS_RT);
NATIVE_KEY_MAPPING.put(NATIVE_KEY_TITLE, METADATA_KEY_TITLE)152         NATIVE_KEY_MAPPING.put(NATIVE_KEY_TITLE, METADATA_KEY_TITLE);
NATIVE_KEY_MAPPING.put(NATIVE_KEY_ARTIST, METADATA_KEY_ARTIST)153         NATIVE_KEY_MAPPING.put(NATIVE_KEY_ARTIST, METADATA_KEY_ARTIST);
NATIVE_KEY_MAPPING.put(NATIVE_KEY_ALBUM, METADATA_KEY_ALBUM)154         NATIVE_KEY_MAPPING.put(NATIVE_KEY_ALBUM, METADATA_KEY_ALBUM);
NATIVE_KEY_MAPPING.put(NATIVE_KEY_GENRE, METADATA_KEY_GENRE)155         NATIVE_KEY_MAPPING.put(NATIVE_KEY_GENRE, METADATA_KEY_GENRE);
NATIVE_KEY_MAPPING.put(NATIVE_KEY_ICON, METADATA_KEY_ICON)156         NATIVE_KEY_MAPPING.put(NATIVE_KEY_ICON, METADATA_KEY_ICON);
NATIVE_KEY_MAPPING.put(NATIVE_KEY_ART, METADATA_KEY_ART)157         NATIVE_KEY_MAPPING.put(NATIVE_KEY_ART, METADATA_KEY_ART);
NATIVE_KEY_MAPPING.put(NATIVE_KEY_CLOCK, METADATA_KEY_CLOCK)158         NATIVE_KEY_MAPPING.put(NATIVE_KEY_CLOCK, METADATA_KEY_CLOCK);
159     }
160 
161     /**
162      * Provides a Clock that can be used to describe time as provided by the Radio.
163      *
164      * The clock is defined by the seconds since epoch at the UTC + 0 timezone
165      * and timezone offset from UTC + 0 represented in number of minutes.
166      *
167      * @hide
168      */
169     @SystemApi
170     public static final class Clock implements Parcelable {
171         private final long mUtcEpochSeconds;
172         private final int mTimezoneOffsetMinutes;
173 
describeContents()174         public int describeContents() {
175             return 0;
176         }
177 
writeToParcel(Parcel out, int flags)178         public void writeToParcel(Parcel out, int flags) {
179             out.writeLong(mUtcEpochSeconds);
180             out.writeInt(mTimezoneOffsetMinutes);
181         }
182 
183         public static final Parcelable.Creator<Clock> CREATOR
184                 = new Parcelable.Creator<Clock>() {
185             public Clock createFromParcel(Parcel in) {
186                 return new Clock(in);
187             }
188 
189             public Clock[] newArray(int size) {
190                 return new Clock[size];
191             }
192         };
193 
Clock(long utcEpochSeconds, int timezoneOffsetMinutes)194         public Clock(long utcEpochSeconds, int timezoneOffsetMinutes) {
195             mUtcEpochSeconds = utcEpochSeconds;
196             mTimezoneOffsetMinutes = timezoneOffsetMinutes;
197         }
198 
Clock(Parcel in)199         private Clock(Parcel in) {
200             mUtcEpochSeconds = in.readLong();
201             mTimezoneOffsetMinutes = in.readInt();
202         }
203 
getUtcEpochSeconds()204         public long getUtcEpochSeconds() {
205             return mUtcEpochSeconds;
206         }
207 
getTimezoneOffsetMinutes()208         public int getTimezoneOffsetMinutes() {
209             return mTimezoneOffsetMinutes;
210         }
211     }
212 
213     private final Bundle mBundle;
214 
RadioMetadata()215     RadioMetadata() {
216         mBundle = new Bundle();
217     }
218 
RadioMetadata(Bundle bundle)219     private RadioMetadata(Bundle bundle) {
220         mBundle = new Bundle(bundle);
221     }
222 
RadioMetadata(Parcel in)223     private RadioMetadata(Parcel in) {
224         mBundle = in.readBundle();
225     }
226 
227     /**
228      * Returns {@code true} if the given key is contained in the meta data
229      *
230      * @param key a String key
231      * @return {@code true} if the key exists in this meta data, {@code false} otherwise
232      */
containsKey(String key)233     public boolean containsKey(String key) {
234         return mBundle.containsKey(key);
235     }
236 
237     /**
238      * Returns the text value associated with the given key as a String, or null
239      * if the key is not found in the meta data.
240      *
241      * @param key The key the value is stored under
242      * @return a String value, or null
243      */
getString(String key)244     public String getString(String key) {
245         return mBundle.getString(key);
246     }
247 
248     /**
249      * Returns the value associated with the given key,
250      * or 0 if the key is not found in the meta data.
251      *
252      * @param key The key the value is stored under
253      * @return an int value
254      */
getInt(String key)255     public int getInt(String key) {
256         return mBundle.getInt(key, 0);
257     }
258 
259     /**
260      * Returns a {@link Bitmap} for the given key or null if the key is not found in the meta data.
261      *
262      * @param key The key the value is stored under
263      * @return a {@link Bitmap} or null
264      */
getBitmap(String key)265     public Bitmap getBitmap(String key) {
266         Bitmap bmp = null;
267         try {
268             bmp = mBundle.getParcelable(key);
269         } catch (Exception e) {
270             // ignore, value was not a bitmap
271             Log.w(TAG, "Failed to retrieve a key as Bitmap.", e);
272         }
273         return bmp;
274     }
275 
getClock(String key)276     public Clock getClock(String key) {
277         Clock clock = null;
278         try {
279             clock = mBundle.getParcelable(key);
280         } catch (Exception e) {
281             // ignore, value was not a clock.
282             Log.w(TAG, "Failed to retrieve a key as Clock.", e);
283         }
284         return clock;
285     }
286 
287     @Override
describeContents()288     public int describeContents() {
289         return 0;
290     }
291 
292     @Override
writeToParcel(Parcel dest, int flags)293     public void writeToParcel(Parcel dest, int flags) {
294         dest.writeBundle(mBundle);
295     }
296 
297     /**
298      * Returns the number of fields in this meta data.
299      *
300      * @return the number of fields in the meta data.
301      */
size()302     public int size() {
303         return mBundle.size();
304     }
305 
306     /**
307      * Returns a Set containing the Strings used as keys in this meta data.
308      *
309      * @return a Set of String keys
310      */
keySet()311     public Set<String> keySet() {
312         return mBundle.keySet();
313     }
314 
315     /**
316      * Helper for getting the String key used by {@link RadioMetadata} from the
317      * corrsponding native integer key.
318      *
319      * @param editorKey The key used by the editor
320      * @return the key used by this class or null if no mapping exists
321      * @hide
322      */
getKeyFromNativeKey(int nativeKey)323     public static String getKeyFromNativeKey(int nativeKey) {
324         return NATIVE_KEY_MAPPING.get(nativeKey, null);
325     }
326 
327     public static final Parcelable.Creator<RadioMetadata> CREATOR =
328             new Parcelable.Creator<RadioMetadata>() {
329                 @Override
330                 public RadioMetadata createFromParcel(Parcel in) {
331                     return new RadioMetadata(in);
332                 }
333 
334                 @Override
335                 public RadioMetadata[] newArray(int size) {
336                     return new RadioMetadata[size];
337                 }
338             };
339 
340     /**
341      * Use to build RadioMetadata objects.
342      */
343     public static final class Builder {
344         private final Bundle mBundle;
345 
346         /**
347          * Create an empty Builder. Any field that should be included in the
348          * {@link RadioMetadata} must be added.
349          */
Builder()350         public Builder() {
351             mBundle = new Bundle();
352         }
353 
354         /**
355          * Create a Builder using a {@link RadioMetadata} instance to set the
356          * initial values. All fields in the source meta data will be included in
357          * the new meta data. Fields can be overwritten by adding the same key.
358          *
359          * @param source
360          */
Builder(RadioMetadata source)361         public Builder(RadioMetadata source) {
362             mBundle = new Bundle(source.mBundle);
363         }
364 
365         /**
366          * Create a Builder using a {@link RadioMetadata} instance to set
367          * initial values, but replace bitmaps with a scaled down copy if they
368          * are larger than maxBitmapSize.
369          *
370          * @param source The original meta data to copy.
371          * @param maxBitmapSize The maximum height/width for bitmaps contained
372          *            in the meta data.
373          * @hide
374          */
Builder(RadioMetadata source, int maxBitmapSize)375         public Builder(RadioMetadata source, int maxBitmapSize) {
376             this(source);
377             for (String key : mBundle.keySet()) {
378                 Object value = mBundle.get(key);
379                 if (value != null && value instanceof Bitmap) {
380                     Bitmap bmp = (Bitmap) value;
381                     if (bmp.getHeight() > maxBitmapSize || bmp.getWidth() > maxBitmapSize) {
382                         putBitmap(key, scaleBitmap(bmp, maxBitmapSize));
383                     }
384                 }
385             }
386         }
387 
388         /**
389          * Put a String value into the meta data. Custom keys may be used, but if
390          * the METADATA_KEYs defined in this class are used they may only be one
391          * of the following:
392          * <ul>
393          * <li>{@link #METADATA_KEY_RDS_PI}</li>
394          * <li>{@link #METADATA_KEY_RDS_PS}</li>
395          * <li>{@link #METADATA_KEY_RDS_RT}</li>
396          * <li>{@link #METADATA_KEY_TITLE}</li>
397          * <li>{@link #METADATA_KEY_ARTIST}</li>
398          * <li>{@link #METADATA_KEY_ALBUM}</li>
399          * <li>{@link #METADATA_KEY_GENRE}</li>
400          * </ul>
401          *
402          * @param key The key for referencing this value
403          * @param value The String value to store
404          * @return the same Builder instance
405          */
putString(String key, String value)406         public Builder putString(String key, String value) {
407             if (!METADATA_KEYS_TYPE.containsKey(key) ||
408                     METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_TEXT) {
409                 throw new IllegalArgumentException("The " + key
410                         + " key cannot be used to put a String");
411             }
412             mBundle.putString(key, value);
413             return this;
414         }
415 
416         /**
417          * Put an int value into the meta data. Custom keys may be used, but if
418          * the METADATA_KEYs defined in this class are used they may only be one
419          * of the following:
420          * <ul>
421          * <li>{@link #METADATA_KEY_RDS_PTY}</li>
422          * <li>{@link #METADATA_KEY_RBDS_PTY}</li>
423          * </ul>
424          *
425          * @param key The key for referencing this value
426          * @param value The int value to store
427          * @return the same Builder instance
428          */
putInt(String key, int value)429         public Builder putInt(String key, int value) {
430             if (!METADATA_KEYS_TYPE.containsKey(key) ||
431                     METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_INT) {
432                 throw new IllegalArgumentException("The " + key
433                         + " key cannot be used to put a long");
434             }
435             mBundle.putInt(key, value);
436             return this;
437         }
438 
439         /**
440          * Put a {@link Bitmap} into the meta data. Custom keys may be used, but
441          * if the METADATA_KEYs defined in this class are used they may only be
442          * one of the following:
443          * <ul>
444          * <li>{@link #METADATA_KEY_ICON}</li>
445          * <li>{@link #METADATA_KEY_ART}</li>
446          * </ul>
447          * <p>
448          *
449          * @param key The key for referencing this value
450          * @param value The Bitmap to store
451          * @return the same Builder instance
452          */
putBitmap(String key, Bitmap value)453         public Builder putBitmap(String key, Bitmap value) {
454             if (!METADATA_KEYS_TYPE.containsKey(key) ||
455                     METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_BITMAP) {
456                 throw new IllegalArgumentException("The " + key
457                         + " key cannot be used to put a Bitmap");
458             }
459             mBundle.putParcelable(key, value);
460             return this;
461         }
462 
463         /**
464          * Put a {@link RadioMetadata.Clock} into the meta data. Custom keys may be used, but if the
465          * METADATA_KEYs defined in this class are used they may only be one of the following:
466          * <ul>
467          * <li>{@link #MEADATA_KEY_CLOCK}</li>
468          * </ul>
469          *
470          * @param utcSecondsSinceEpoch Number of seconds since epoch for UTC + 0 timezone.
471          * @param timezoneOffsetInMinutes Offset of timezone from UTC + 0 in minutes.
472          * @return the same Builder instance.
473          */
putClock(String key, long utcSecondsSinceEpoch, int timezoneOffsetMinutes)474         public Builder putClock(String key, long utcSecondsSinceEpoch, int timezoneOffsetMinutes) {
475             if (!METADATA_KEYS_TYPE.containsKey(key) ||
476                     METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_CLOCK) {
477                 throw new IllegalArgumentException("The " + key
478                     + " key cannot be used to put a RadioMetadata.Clock.");
479             }
480             mBundle.putParcelable(key, new Clock(utcSecondsSinceEpoch, timezoneOffsetMinutes));
481             return this;
482         }
483 
484         /**
485          * Creates a {@link RadioMetadata} instance with the specified fields.
486          *
487          * @return a new {@link RadioMetadata} object
488          */
build()489         public RadioMetadata build() {
490             return new RadioMetadata(mBundle);
491         }
492 
scaleBitmap(Bitmap bmp, int maxSize)493         private Bitmap scaleBitmap(Bitmap bmp, int maxSize) {
494             float maxSizeF = maxSize;
495             float widthScale = maxSizeF / bmp.getWidth();
496             float heightScale = maxSizeF / bmp.getHeight();
497             float scale = Math.min(widthScale, heightScale);
498             int height = (int) (bmp.getHeight() * scale);
499             int width = (int) (bmp.getWidth() * scale);
500             return Bitmap.createScaledBitmap(bmp, width, height, true);
501         }
502     }
503 
putIntFromNative(int nativeKey, int value)504     int putIntFromNative(int nativeKey, int value) {
505         String key = getKeyFromNativeKey(nativeKey);
506         if (!METADATA_KEYS_TYPE.containsKey(key) ||
507                 METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_INT) {
508             return -1;
509         }
510         mBundle.putInt(key, value);
511         return 0;
512     }
513 
putStringFromNative(int nativeKey, String value)514     int putStringFromNative(int nativeKey, String value) {
515         String key = getKeyFromNativeKey(nativeKey);
516         if (!METADATA_KEYS_TYPE.containsKey(key) ||
517                 METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_TEXT) {
518             return -1;
519         }
520         mBundle.putString(key, value);
521         return 0;
522     }
523 
putBitmapFromNative(int nativeKey, byte[] value)524     int putBitmapFromNative(int nativeKey, byte[] value) {
525         String key = getKeyFromNativeKey(nativeKey);
526         if (!METADATA_KEYS_TYPE.containsKey(key) ||
527                 METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_BITMAP) {
528             return -1;
529         }
530         Bitmap bmp = null;
531         try {
532             bmp = BitmapFactory.decodeByteArray(value, 0, value.length);
533             if (bmp != null) {
534                 mBundle.putParcelable(key, bmp);
535                 return 0;
536             }
537         } catch (Exception e) {
538         }
539         return -1;
540     }
541 
putClockFromNative(int nativeKey, long utcEpochSeconds, int timezoneOffsetInMinutes)542     int putClockFromNative(int nativeKey, long utcEpochSeconds, int timezoneOffsetInMinutes) {
543         Log.d(TAG, "putClockFromNative()");
544         String key = getKeyFromNativeKey(nativeKey);
545         if (!METADATA_KEYS_TYPE.containsKey(key) ||
546                 METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_CLOCK) {
547               return -1;
548         }
549         mBundle.putParcelable(key, new RadioMetadata.Clock(
550             utcEpochSeconds, timezoneOffsetInMinutes));
551         return 0;
552     }
553 }
554