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