blob: 0cf7605c98a20fa6562b2b03ef334a35d0c3343d [file] [log] [blame]
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -07001/**
2 * Copyright (C) 2017 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
17package android.hardware.radio;
18
19import android.annotation.IntDef;
Tomasz Wasilczykd2b5cfb2017-08-04 12:56:47 -070020import android.annotation.IntRange;
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -070021import android.annotation.NonNull;
22import android.annotation.Nullable;
23import android.annotation.SystemApi;
24import android.os.Parcel;
25import android.os.Parcelable;
26
27import java.lang.annotation.Retention;
28import java.lang.annotation.RetentionPolicy;
29import java.util.ArrayList;
30import java.util.List;
31import java.util.Objects;
32import java.util.stream.Stream;
33
34/**
35 * A set of identifiers necessary to tune to a given station.
36 *
37 * This can hold various identifiers, like
38 * - AM/FM frequency
39 * - HD Radio subchannel
40 * - DAB channel info
41 *
42 * The primary ID uniquely identifies a station and can be used for equality
43 * check. The secondary IDs are supplementary and can speed up tuning process,
44 * but the primary ID is sufficient (ie. after a full band scan).
45 *
46 * Two selectors with different secondary IDs, but the same primary ID are
47 * considered equal. In particular, secondary IDs vector may get updated for
48 * an entry on the program list (ie. when a better frequency for a given
49 * station is found).
50 *
51 * The primaryId of a given programType MUST be of a specific type:
52 * - AM, FM: RDS_PI if the station broadcasts RDS, AMFM_FREQUENCY otherwise;
53 * - AM_HD, FM_HD: HD_STATION_ID_EXT;
54 * - DAB: DAB_SIDECC;
55 * - DRMO: DRMO_SERVICE_ID;
56 * - SXM: SXM_SERVICE_ID;
57 * - VENDOR: VENDOR_PRIMARY.
58 * @hide
59 */
60@SystemApi
61public final class ProgramSelector implements Parcelable {
Tomasz Wasilczykfae3d0d2018-01-13 11:24:16 -080062 /** Invalid program type.
63 * @deprecated use {@link ProgramIdentifier} instead
64 */
65 @Deprecated
Tomasz Wasilczyk436128f2018-01-08 16:46:09 -080066 public static final int PROGRAM_TYPE_INVALID = 0;
Tomasz Wasilczykfae3d0d2018-01-13 11:24:16 -080067 /** Analogue AM radio (with or without RDS).
68 * @deprecated use {@link ProgramIdentifier} instead
69 */
70 @Deprecated
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -070071 public static final int PROGRAM_TYPE_AM = 1;
Tomasz Wasilczykfae3d0d2018-01-13 11:24:16 -080072 /** analogue FM radio (with or without RDS).
73 * @deprecated use {@link ProgramIdentifier} instead
74 */
75 @Deprecated
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -070076 public static final int PROGRAM_TYPE_FM = 2;
Tomasz Wasilczykfae3d0d2018-01-13 11:24:16 -080077 /** AM HD Radio.
78 * @deprecated use {@link ProgramIdentifier} instead
79 */
80 @Deprecated
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -070081 public static final int PROGRAM_TYPE_AM_HD = 3;
Tomasz Wasilczykfae3d0d2018-01-13 11:24:16 -080082 /** FM HD Radio.
83 * @deprecated use {@link ProgramIdentifier} instead
84 */
85 @Deprecated
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -070086 public static final int PROGRAM_TYPE_FM_HD = 4;
Tomasz Wasilczykfae3d0d2018-01-13 11:24:16 -080087 /** Digital audio broadcasting.
88 * @deprecated use {@link ProgramIdentifier} instead
89 */
90 @Deprecated
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -070091 public static final int PROGRAM_TYPE_DAB = 5;
Tomasz Wasilczykfae3d0d2018-01-13 11:24:16 -080092 /** Digital Radio Mondiale.
93 * @deprecated use {@link ProgramIdentifier} instead
94 */
95 @Deprecated
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -070096 public static final int PROGRAM_TYPE_DRMO = 6;
Tomasz Wasilczykfae3d0d2018-01-13 11:24:16 -080097 /** SiriusXM Satellite Radio.
98 * @deprecated use {@link ProgramIdentifier} instead
99 */
100 @Deprecated
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -0700101 public static final int PROGRAM_TYPE_SXM = 7;
Tomasz Wasilczykfae3d0d2018-01-13 11:24:16 -0800102 /** Vendor-specific, not synced across devices.
103 * @deprecated use {@link ProgramIdentifier} instead
104 */
105 @Deprecated
Tomasz Wasilczykd2b5cfb2017-08-04 12:56:47 -0700106 public static final int PROGRAM_TYPE_VENDOR_START = 1000;
Tomasz Wasilczykfae3d0d2018-01-13 11:24:16 -0800107 /** @deprecated use {@link ProgramIdentifier} instead */
108 @Deprecated
Tomasz Wasilczykd2b5cfb2017-08-04 12:56:47 -0700109 public static final int PROGRAM_TYPE_VENDOR_END = 1999;
Tomasz Wasilczykfae3d0d2018-01-13 11:24:16 -0800110 /** @deprecated use {@link ProgramIdentifier} instead */
111 @Deprecated
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -0700112 @IntDef(prefix = { "PROGRAM_TYPE_" }, value = {
Tomasz Wasilczyk436128f2018-01-08 16:46:09 -0800113 PROGRAM_TYPE_INVALID,
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -0700114 PROGRAM_TYPE_AM,
115 PROGRAM_TYPE_FM,
116 PROGRAM_TYPE_AM_HD,
117 PROGRAM_TYPE_FM_HD,
118 PROGRAM_TYPE_DAB,
119 PROGRAM_TYPE_DRMO,
120 PROGRAM_TYPE_SXM,
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -0700121 })
Tomasz Wasilczykd2b5cfb2017-08-04 12:56:47 -0700122 @IntRange(from = PROGRAM_TYPE_VENDOR_START, to = PROGRAM_TYPE_VENDOR_END)
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -0700123 @Retention(RetentionPolicy.SOURCE)
124 public @interface ProgramType {}
125
Tomasz Wasilczyk436128f2018-01-08 16:46:09 -0800126 public static final int IDENTIFIER_TYPE_INVALID = 0;
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -0700127 /** kHz */
128 public static final int IDENTIFIER_TYPE_AMFM_FREQUENCY = 1;
129 /** 16bit */
130 public static final int IDENTIFIER_TYPE_RDS_PI = 2;
131 /**
132 * 64bit compound primary identifier for HD Radio.
133 *
134 * Consists of (from the LSB):
135 * - 32bit: Station ID number;
136 * - 4bit: HD_SUBCHANNEL;
137 * - 18bit: AMFM_FREQUENCY.
138 * The remaining bits should be set to zeros when writing on the chip side
139 * and ignored when read.
140 */
141 public static final int IDENTIFIER_TYPE_HD_STATION_ID_EXT = 3;
142 /**
143 * HD Radio subchannel - a value of range 0-7.
144 *
145 * The subchannel index is 0-based (where 0 is MPS and 1..7 are SPS),
146 * as opposed to HD Radio standard (where it's 1-based).
Tomasz Wasilczykfae3d0d2018-01-13 11:24:16 -0800147 *
148 * @deprecated use IDENTIFIER_TYPE_HD_STATION_ID_EXT instead
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -0700149 */
Tomasz Wasilczykfae3d0d2018-01-13 11:24:16 -0800150 @Deprecated
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -0700151 public static final int IDENTIFIER_TYPE_HD_SUBCHANNEL = 4;
152 /**
Tomasz Wasilczykfae3d0d2018-01-13 11:24:16 -0800153 * 64bit additional identifier for HD Radio.
154 *
155 * Due to Station ID abuse, some HD_STATION_ID_EXT identifiers may be not
156 * globally unique. To provide a best-effort solution, a short version of
157 * station name may be carried as additional identifier and may be used
158 * by the tuner hardware to double-check tuning.
159 *
160 * The name is limited to the first 8 A-Z0-9 characters (lowercase letters
161 * must be converted to uppercase). Encoded in little-endian ASCII:
162 * the first character of the name is the LSB.
163 *
164 * For example: "Abc" is encoded as 0x434241.
165 */
166 public static final int IDENTIFIER_TYPE_HD_STATION_NAME = 10004;
167 /**
168 * @see {@link IDENTIFIER_TYPE_DAB_SID_EXT}
169 */
170 public static final int IDENTIFIER_TYPE_DAB_SIDECC = 5;
171 /**
172 * 28bit compound primary identifier for Digital Audio Broadcasting.
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -0700173 *
174 * Consists of (from the LSB):
175 * - 16bit: SId;
Tomasz Wasilczykfae3d0d2018-01-13 11:24:16 -0800176 * - 8bit: ECC code;
177 * - 4bit: SCIdS.
178 *
179 * SCIdS (Service Component Identifier within the Service) value
180 * of 0 represents the main service, while 1 and above represents
181 * secondary services.
182 *
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -0700183 * The remaining bits should be set to zeros when writing on the chip side
184 * and ignored when read.
185 */
Tomasz Wasilczykfae3d0d2018-01-13 11:24:16 -0800186 public static final int IDENTIFIER_TYPE_DAB_SID_EXT = IDENTIFIER_TYPE_DAB_SIDECC;
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -0700187 /** 16bit */
188 public static final int IDENTIFIER_TYPE_DAB_ENSEMBLE = 6;
189 /** 12bit */
190 public static final int IDENTIFIER_TYPE_DAB_SCID = 7;
191 /** kHz */
192 public static final int IDENTIFIER_TYPE_DAB_FREQUENCY = 8;
193 /** 24bit */
194 public static final int IDENTIFIER_TYPE_DRMO_SERVICE_ID = 9;
195 /** kHz */
196 public static final int IDENTIFIER_TYPE_DRMO_FREQUENCY = 10;
Tomasz Wasilczykfae3d0d2018-01-13 11:24:16 -0800197 /**
198 * 1: AM, 2:FM
199 * @deprecated use {@link IDENTIFIER_TYPE_DRMO_FREQUENCY} instead
200 */
201 @Deprecated
Tomasz Wasilczykea0302a2017-07-17 15:40:33 -0700202 public static final int IDENTIFIER_TYPE_DRMO_MODULATION = 11;
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -0700203 /** 32bit */
Tomasz Wasilczykea0302a2017-07-17 15:40:33 -0700204 public static final int IDENTIFIER_TYPE_SXM_SERVICE_ID = 12;
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -0700205 /** 0-999 range */
Tomasz Wasilczykea0302a2017-07-17 15:40:33 -0700206 public static final int IDENTIFIER_TYPE_SXM_CHANNEL = 13;
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -0700207 /**
208 * Primary identifier for vendor-specific radio technology.
209 * The value format is determined by a vendor.
210 *
Tomasz Wasilczykd2b5cfb2017-08-04 12:56:47 -0700211 * It must not be used in any other programType than corresponding VENDOR
212 * type between VENDOR_START and VENDOR_END (eg. identifier type 1015 must
213 * not be used in any program type other than 1015).
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -0700214 */
Tomasz Wasilczykfae3d0d2018-01-13 11:24:16 -0800215 public static final int IDENTIFIER_TYPE_VENDOR_START = PROGRAM_TYPE_VENDOR_START;
216 /**
217 * @see {@link IDENTIFIER_TYPE_VENDOR_START}
218 */
219 public static final int IDENTIFIER_TYPE_VENDOR_END = PROGRAM_TYPE_VENDOR_END;
220 /**
221 * @deprecated use {@link IDENTIFIER_TYPE_VENDOR_START} instead
222 */
223 @Deprecated
224 public static final int IDENTIFIER_TYPE_VENDOR_PRIMARY_START = IDENTIFIER_TYPE_VENDOR_START;
225 /**
226 * @deprecated use {@link IDENTIFIER_TYPE_VENDOR_END} instead
227 */
228 @Deprecated
229 public static final int IDENTIFIER_TYPE_VENDOR_PRIMARY_END = IDENTIFIER_TYPE_VENDOR_END;
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -0700230 @IntDef(prefix = { "IDENTIFIER_TYPE_" }, value = {
Tomasz Wasilczyk436128f2018-01-08 16:46:09 -0800231 IDENTIFIER_TYPE_INVALID,
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -0700232 IDENTIFIER_TYPE_AMFM_FREQUENCY,
233 IDENTIFIER_TYPE_RDS_PI,
234 IDENTIFIER_TYPE_HD_STATION_ID_EXT,
235 IDENTIFIER_TYPE_HD_SUBCHANNEL,
Tomasz Wasilczykfae3d0d2018-01-13 11:24:16 -0800236 IDENTIFIER_TYPE_HD_STATION_NAME,
237 IDENTIFIER_TYPE_DAB_SID_EXT,
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -0700238 IDENTIFIER_TYPE_DAB_SIDECC,
239 IDENTIFIER_TYPE_DAB_ENSEMBLE,
240 IDENTIFIER_TYPE_DAB_SCID,
241 IDENTIFIER_TYPE_DAB_FREQUENCY,
242 IDENTIFIER_TYPE_DRMO_SERVICE_ID,
243 IDENTIFIER_TYPE_DRMO_FREQUENCY,
Tomasz Wasilczykea0302a2017-07-17 15:40:33 -0700244 IDENTIFIER_TYPE_DRMO_MODULATION,
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -0700245 IDENTIFIER_TYPE_SXM_SERVICE_ID,
246 IDENTIFIER_TYPE_SXM_CHANNEL,
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -0700247 })
Tomasz Wasilczykfae3d0d2018-01-13 11:24:16 -0800248 @IntRange(from = IDENTIFIER_TYPE_VENDOR_START, to = IDENTIFIER_TYPE_VENDOR_END)
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -0700249 @Retention(RetentionPolicy.SOURCE)
250 public @interface IdentifierType {}
251
252 private final @ProgramType int mProgramType;
253 private final @NonNull Identifier mPrimaryId;
254 private final @NonNull Identifier[] mSecondaryIds;
255 private final @NonNull long[] mVendorIds;
256
257 /**
258 * Constructor for ProgramSelector.
259 *
260 * It's not desired to modify selector objects, so all its fields are initialized at creation.
261 *
262 * Identifier lists must not contain any nulls, but can itself be null to be interpreted
263 * as empty list at object creation.
264 *
265 * @param programType type of a radio technology.
266 * @param primaryId primary program identifier.
267 * @param secondaryIds list of secondary program identifiers.
268 * @param vendorIds list of vendor-specific program identifiers.
269 */
270 public ProgramSelector(@ProgramType int programType, @NonNull Identifier primaryId,
271 @Nullable Identifier[] secondaryIds, @Nullable long[] vendorIds) {
272 if (secondaryIds == null) secondaryIds = new Identifier[0];
273 if (vendorIds == null) vendorIds = new long[0];
274 if (Stream.of(secondaryIds).anyMatch(id -> id == null)) {
275 throw new IllegalArgumentException("secondaryIds list must not contain nulls");
276 }
277 mProgramType = programType;
278 mPrimaryId = Objects.requireNonNull(primaryId);
279 mSecondaryIds = secondaryIds;
280 mVendorIds = vendorIds;
281 }
282
283 /**
284 * Type of a radio technology.
285 *
Jeff Sharkey67f9d502017-08-05 13:49:13 -0600286 * @return program type.
Tomasz Wasilczykfae3d0d2018-01-13 11:24:16 -0800287 * @deprecated use {@link getPrimaryId} instead
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -0700288 */
Tomasz Wasilczykfae3d0d2018-01-13 11:24:16 -0800289 @Deprecated
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -0700290 public @ProgramType int getProgramType() {
291 return mProgramType;
292 }
293
294 /**
295 * Primary program identifier uniquely identifies a station and is used to
296 * determine equality between two ProgramSelectors.
297 *
Jeff Sharkey67f9d502017-08-05 13:49:13 -0600298 * @return primary identifier.
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -0700299 */
300 public @NonNull Identifier getPrimaryId() {
301 return mPrimaryId;
302 }
303
304 /**
305 * Secondary program identifier is not required for tuning, but may make it
306 * faster or more reliable.
307 *
Jeff Sharkey67f9d502017-08-05 13:49:13 -0600308 * @return secondary identifier list, must not be modified.
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -0700309 */
310 public @NonNull Identifier[] getSecondaryIds() {
311 return mSecondaryIds;
312 }
313
314 /**
315 * Looks up an identifier of a given type (either primary or secondary).
316 *
317 * If there are multiple identifiers if a given type, then first in order (where primary id is
318 * before any secondary) is selected.
319 *
320 * @param type type of identifier.
321 * @return identifier value, if found.
322 * @throws IllegalArgumentException, if not found.
323 */
324 public long getFirstId(@IdentifierType int type) {
325 if (mPrimaryId.getType() == type) return mPrimaryId.getValue();
326 for (Identifier id : mSecondaryIds) {
327 if (id.getType() == type) return id.getValue();
328 }
329 throw new IllegalArgumentException("Identifier " + type + " not found");
330 }
331
332 /**
333 * Looks up all identifier of a given type (either primary or secondary).
334 *
335 * Some identifiers may be provided multiple times, for example
336 * IDENTIFIER_TYPE_AMFM_FREQUENCY for FM Alternate Frequencies.
337 *
338 * @param type type of identifier.
339 * @return a list of identifiers, generated on each call. May be modified.
340 */
341 public @NonNull Identifier[] getAllIds(@IdentifierType int type) {
342 List<Identifier> out = new ArrayList<>();
343
344 if (mPrimaryId.getType() == type) out.add(mPrimaryId);
345 for (Identifier id : mSecondaryIds) {
346 if (id.getType() == type) out.add(id);
347 }
348
349 return out.toArray(new Identifier[out.size()]);
350 }
351
352 /**
353 * Vendor identifiers are passed as-is to the HAL implementation,
354 * preserving elements order.
355 *
Tomasz Wasilczyk436128f2018-01-08 16:46:09 -0800356 * @return an array of vendor identifiers, must not be modified.
Tomasz Wasilczykfae3d0d2018-01-13 11:24:16 -0800357 * @deprecated for HAL 1.x compatibility;
358 * HAL 2.x uses standard primary/secondary lists for vendor IDs
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -0700359 */
Tomasz Wasilczykfae3d0d2018-01-13 11:24:16 -0800360 @Deprecated
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -0700361 public @NonNull long[] getVendorIds() {
362 return mVendorIds;
363 }
364
365 /**
366 * Builds new ProgramSelector for AM/FM frequency.
367 *
368 * @param band the band.
369 * @param frequencyKhz the frequency in kHz.
370 * @return new ProgramSelector object representing given frequency.
371 * @throws IllegalArgumentException if provided frequency is out of bounds.
372 */
373 public static @NonNull ProgramSelector createAmFmSelector(
374 @RadioManager.Band int band, int frequencyKhz) {
375 return createAmFmSelector(band, frequencyKhz, 0);
376 }
377
378 /**
379 * Checks, if a given AM/FM frequency is roughly valid and in correct unit.
380 *
381 * It does not check the range precisely. In particular, it may be way off for certain regions.
382 * The main purpose is to avoid passing inproper units, ie. MHz instead of kHz.
383 *
384 * @param isAm true, if AM, false if FM.
385 * @param frequencyKhz the frequency in kHz.
386 * @return true, if the frequency is rougly valid.
387 */
388 private static boolean isValidAmFmFrequency(boolean isAm, int frequencyKhz) {
389 if (isAm) {
390 return frequencyKhz > 150 && frequencyKhz < 30000;
391 } else {
392 return frequencyKhz > 60000 && frequencyKhz < 110000;
393 }
394 }
395
396 /**
397 * Builds new ProgramSelector for AM/FM frequency.
398 *
399 * This method variant supports HD Radio subchannels, but it's undesirable to
400 * select them manually. Instead, the value should be retrieved from program list.
401 *
402 * @param band the band.
403 * @param frequencyKhz the frequency in kHz.
404 * @param subChannel 1-based HD Radio subchannel.
405 * @return new ProgramSelector object representing given frequency.
406 * @throws IllegalArgumentException if provided frequency is out of bounds,
407 * or tried setting a subchannel for analog AM/FM.
408 */
409 public static @NonNull ProgramSelector createAmFmSelector(
410 @RadioManager.Band int band, int frequencyKhz, int subChannel) {
411 boolean isAm = (band == RadioManager.BAND_AM || band == RadioManager.BAND_AM_HD);
412 boolean isDigital = (band == RadioManager.BAND_AM_HD || band == RadioManager.BAND_FM_HD);
413 if (!isAm && !isDigital && band != RadioManager.BAND_FM) {
414 throw new IllegalArgumentException("Unknown band: " + band);
415 }
416 if (subChannel < 0 || subChannel > 8) {
417 throw new IllegalArgumentException("Invalid subchannel: " + subChannel);
418 }
419 if (subChannel > 0 && !isDigital) {
420 throw new IllegalArgumentException("Subchannels are not supported for non-HD radio");
421 }
422 if (!isValidAmFmFrequency(isAm, frequencyKhz)) {
423 throw new IllegalArgumentException("Provided value is not a valid AM/FM frequency");
424 }
425
426 // We can't use AM_HD or FM_HD, because we don't know HD station ID.
427 @ProgramType int programType = isAm ? PROGRAM_TYPE_AM : PROGRAM_TYPE_FM;
428 Identifier primary = new Identifier(IDENTIFIER_TYPE_AMFM_FREQUENCY, frequencyKhz);
429
430 Identifier[] secondary = null;
431 if (subChannel > 0) {
432 /* Stating sub channel for non-HD AM/FM does not give any guarantees,
433 * but we can't do much more without HD station ID.
434 *
435 * The legacy APIs had 1-based subChannels, while ProgramSelector is 0-based.
436 */
437 secondary = new Identifier[]{
438 new Identifier(IDENTIFIER_TYPE_HD_SUBCHANNEL, subChannel - 1)};
439 }
440
441 return new ProgramSelector(programType, primary, secondary, null);
442 }
443
444 @Override
445 public String toString() {
446 StringBuilder sb = new StringBuilder("ProgramSelector(type=").append(mProgramType)
447 .append(", primary=").append(mPrimaryId);
448 if (mSecondaryIds.length > 0) sb.append(", secondary=").append(mSecondaryIds);
449 if (mVendorIds.length > 0) sb.append(", vendor=").append(mVendorIds);
450 sb.append(")");
451 return sb.toString();
452 }
453
454 @Override
455 public int hashCode() {
456 // secondaryIds and vendorIds are ignored for equality/hashing
457 return Objects.hash(mProgramType, mPrimaryId);
458 }
459
460 @Override
461 public boolean equals(Object obj) {
462 if (this == obj) return true;
463 if (!(obj instanceof ProgramSelector)) return false;
464 ProgramSelector other = (ProgramSelector) obj;
465 // secondaryIds and vendorIds are ignored for equality/hashing
466 return other.getProgramType() == mProgramType && mPrimaryId.equals(other.getPrimaryId());
467 }
468
469 private ProgramSelector(Parcel in) {
470 mProgramType = in.readInt();
471 mPrimaryId = in.readTypedObject(Identifier.CREATOR);
472 mSecondaryIds = in.createTypedArray(Identifier.CREATOR);
473 if (Stream.of(mSecondaryIds).anyMatch(id -> id == null)) {
474 throw new IllegalArgumentException("secondaryIds list must not contain nulls");
475 }
476 mVendorIds = in.createLongArray();
477 }
478
479 @Override
480 public void writeToParcel(Parcel dest, int flags) {
481 dest.writeInt(mProgramType);
482 dest.writeTypedObject(mPrimaryId, 0);
483 dest.writeTypedArray(mSecondaryIds, 0);
484 dest.writeLongArray(mVendorIds);
485 }
486
487 @Override
488 public int describeContents() {
489 return 0;
490 }
491
492 public static final Parcelable.Creator<ProgramSelector> CREATOR =
493 new Parcelable.Creator<ProgramSelector>() {
494 public ProgramSelector createFromParcel(Parcel in) {
495 return new ProgramSelector(in);
496 }
497
498 public ProgramSelector[] newArray(int size) {
499 return new ProgramSelector[size];
500 }
501 };
502
503 /**
504 * A single program identifier component, eg. frequency or channel ID.
505 *
506 * The long value field holds the value in format described in comments for
507 * IdentifierType constants.
508 */
509 public static final class Identifier implements Parcelable {
510 private final @IdentifierType int mType;
511 private final long mValue;
512
513 public Identifier(@IdentifierType int type, long value) {
Tomasz Wasilczykfae3d0d2018-01-13 11:24:16 -0800514 if (type == IDENTIFIER_TYPE_HD_STATION_NAME) {
515 // see getType
516 type = IDENTIFIER_TYPE_HD_SUBCHANNEL;
517 }
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -0700518 mType = type;
519 mValue = value;
520 }
521
522 /**
523 * Type of an identifier.
524 *
525 * @return type of an identifier.
526 */
527 public @IdentifierType int getType() {
Tomasz Wasilczykfae3d0d2018-01-13 11:24:16 -0800528 if (mType == IDENTIFIER_TYPE_HD_SUBCHANNEL && mValue > 10) {
529 /* HD_SUBCHANNEL and HD_STATION_NAME use the same identifier type, but they differ
530 * in possible values: sub channel is 0-7, station name is greater than ASCII space
531 * code (32).
532 */
533 return IDENTIFIER_TYPE_HD_STATION_NAME;
534 }
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -0700535 return mType;
536 }
537
538 /**
539 * Value of an identifier.
540 *
541 * Its meaning depends on identifier type, ie. for IDENTIFIER_TYPE_AMFM_FREQUENCY type,
542 * the value is a frequency in kHz.
543 *
544 * The range of a value depends on its type; it does not always require the whole long
545 * range. Casting to necessary type (ie. int) without range checking is correct in front-end
546 * code - any range violations are either errors in the framework or in the
547 * HAL implementation. For example, IDENTIFIER_TYPE_AMFM_FREQUENCY always fits in int,
548 * as Integer.MAX_VALUE would mean 2.1THz.
549 *
550 * @return value of an identifier.
551 */
552 public long getValue() {
553 return mValue;
554 }
555
556 @Override
557 public String toString() {
558 return "Identifier(" + mType + ", " + mValue + ")";
559 }
560
561 @Override
562 public int hashCode() {
563 return Objects.hash(mType, mValue);
564 }
565
566 @Override
567 public boolean equals(Object obj) {
568 if (this == obj) return true;
569 if (!(obj instanceof Identifier)) return false;
570 Identifier other = (Identifier) obj;
571 return other.getType() == mType && other.getValue() == mValue;
572 }
573
574 private Identifier(Parcel in) {
575 mType = in.readInt();
576 mValue = in.readLong();
577 }
578
579 @Override
580 public void writeToParcel(Parcel dest, int flags) {
581 dest.writeInt(mType);
582 dest.writeLong(mValue);
583 }
584
585 @Override
586 public int describeContents() {
587 return 0;
588 }
589
590 public static final Parcelable.Creator<Identifier> CREATOR =
591 new Parcelable.Creator<Identifier>() {
592 public Identifier createFromParcel(Parcel in) {
593 return new Identifier(in);
594 }
595
596 public Identifier[] newArray(int size) {
597 return new Identifier[size];
598 }
599 };
600 }
601}