blob: 92653d188893c9d5fe521f427df09aa6a1f7c82a [file] [log] [blame]
Eric Laurent2035ac82015-03-05 15:18:44 -08001/**
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
17package android.hardware.radio;
18
Tomasz Wasilczyk749e3dc2017-07-28 13:20:41 -070019import android.Manifest;
Tomasz Wasilczykf151a7b2018-01-11 16:03:46 -080020import android.annotation.CallbackExecutor;
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -070021import android.annotation.IntDef;
Tomasz Wasilczyk6849aa82017-03-29 11:59:23 -070022import android.annotation.NonNull;
Tomasz Wasilczykeab3e552018-01-11 20:12:10 -080023import android.annotation.Nullable;
Jeff Sharkey98af2e42018-02-16 10:14:57 -070024import android.annotation.RequiresFeature;
Tomasz Wasilczyk749e3dc2017-07-28 13:20:41 -070025import android.annotation.RequiresPermission;
Eric Laurent2035ac82015-03-05 15:18:44 -080026import android.annotation.SystemApi;
Jeff Sharkeyd86b8fe2017-06-02 17:36:26 -060027import android.annotation.SystemService;
Eric Laurent2035ac82015-03-05 15:18:44 -080028import android.content.Context;
Jeff Sharkey98af2e42018-02-16 10:14:57 -070029import android.content.pm.PackageManager;
Eric Laurent2035ac82015-03-05 15:18:44 -080030import android.os.Handler;
31import android.os.Parcel;
32import android.os.Parcelable;
Tomasz Wasilczyk347192e2017-04-04 11:13:44 -070033import android.os.RemoteException;
34import android.os.ServiceManager;
35import android.os.ServiceManager.ServiceNotFoundException;
Tomasz Wasilczyk6849aa82017-03-29 11:59:23 -070036import android.text.TextUtils;
Tomasz Wasilczyk347192e2017-04-04 11:13:44 -070037import android.util.Log;
38
Tomasz Wasilczykeab3e552018-01-11 20:12:10 -080039import com.android.internal.util.Preconditions;
40
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -070041import java.lang.annotation.Retention;
42import java.lang.annotation.RetentionPolicy;
Eric Laurent2035ac82015-03-05 15:18:44 -080043import java.util.Arrays;
Tomasz Wasilczykeab3e552018-01-11 20:12:10 -080044import java.util.Collection;
45import java.util.Collections;
Tomasz Wasilczyk0f1776d2017-08-03 11:03:49 -070046import java.util.HashMap;
Tomasz Wasilczykd15c9df2017-06-13 10:10:36 -070047import java.util.List;
Tomasz Wasilczyk0f1776d2017-08-03 11:03:49 -070048import java.util.Map;
Tomasz Wasilczykf58305d2017-12-28 14:03:15 -080049import java.util.Objects;
Tomasz Wasilczyk54587ce2017-07-16 15:15:40 -070050import java.util.Set;
Tomasz Wasilczykf151a7b2018-01-11 16:03:46 -080051import java.util.concurrent.Executor;
Tomasz Wasilczyk54587ce2017-07-16 15:15:40 -070052import java.util.stream.Collectors;
Eric Laurent2035ac82015-03-05 15:18:44 -080053
54/**
55 * The RadioManager class allows to control a broadcast radio tuner present on the device.
56 * It provides data structures and methods to query for available radio modules, list their
57 * properties and open an interface to control tuning operations and receive callbacks when
58 * asynchronous operations complete or events occur.
59 * @hide
60 */
61@SystemApi
Jeff Sharkeyd86b8fe2017-06-02 17:36:26 -060062@SystemService(Context.RADIO_SERVICE)
Jeff Sharkey98af2e42018-02-16 10:14:57 -070063@RequiresFeature(PackageManager.FEATURE_BROADCAST_RADIO)
Eric Laurent2035ac82015-03-05 15:18:44 -080064public class RadioManager {
Tomasz Wasilczyk6b4b6462017-07-19 10:52:28 -070065 private static final String TAG = "BroadcastRadio.manager";
Eric Laurent2035ac82015-03-05 15:18:44 -080066
67 /** Method return status: successful operation */
68 public static final int STATUS_OK = 0;
69 /** Method return status: unspecified error */
70 public static final int STATUS_ERROR = Integer.MIN_VALUE;
71 /** Method return status: permission denied */
72 public static final int STATUS_PERMISSION_DENIED = -1;
73 /** Method return status: initialization failure */
74 public static final int STATUS_NO_INIT = -19;
75 /** Method return status: invalid argument provided */
76 public static final int STATUS_BAD_VALUE = -22;
77 /** Method return status: cannot reach service */
78 public static final int STATUS_DEAD_OBJECT = -32;
79 /** Method return status: invalid or out of sequence operation */
80 public static final int STATUS_INVALID_OPERATION = -38;
81 /** Method return status: time out before operation completion */
82 public static final int STATUS_TIMED_OUT = -110;
83
84
85 // keep in sync with radio_class_t in /system/core/incluse/system/radio.h
86 /** Radio module class supporting FM (including HD radio) and AM */
87 public static final int CLASS_AM_FM = 0;
88 /** Radio module class supporting satellite radio */
89 public static final int CLASS_SAT = 1;
90 /** Radio module class supporting Digital terrestrial radio */
91 public static final int CLASS_DT = 2;
92
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -070093 public static final int BAND_INVALID = -1;
Eric Laurent2035ac82015-03-05 15:18:44 -080094 /** AM radio band (LW/MW/SW).
95 * @see BandDescriptor */
96 public static final int BAND_AM = 0;
97 /** FM radio band.
98 * @see BandDescriptor */
99 public static final int BAND_FM = 1;
100 /** FM HD radio or DRM band.
101 * @see BandDescriptor */
102 public static final int BAND_FM_HD = 2;
103 /** AM HD radio or DRM band.
104 * @see BandDescriptor */
105 public static final int BAND_AM_HD = 3;
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -0700106 @IntDef(prefix = { "BAND_" }, value = {
107 BAND_INVALID,
108 BAND_AM,
109 BAND_FM,
110 BAND_AM_HD,
111 BAND_FM_HD,
112 })
113 @Retention(RetentionPolicy.SOURCE)
114 public @interface Band {}
Eric Laurent2035ac82015-03-05 15:18:44 -0800115
116 // keep in sync with radio_region_t in /system/core/incluse/system/radio.h
117 /** Africa, Europe.
118 * @see BandDescriptor */
119 public static final int REGION_ITU_1 = 0;
120 /** Americas.
121 * @see BandDescriptor */
122 public static final int REGION_ITU_2 = 1;
123 /** Russia.
124 * @see BandDescriptor */
125 public static final int REGION_OIRT = 2;
126 /** Japan.
127 * @see BandDescriptor */
128 public static final int REGION_JAPAN = 3;
129 /** Korea.
130 * @see BandDescriptor */
131 public static final int REGION_KOREA = 4;
132
Tomasz Wasilczykce40fe92018-01-04 20:52:39 -0800133 /**
134 * Forces mono audio stream reception.
135 *
136 * Analog broadcasts can recover poor reception conditions by jointing
137 * stereo channels into one. Mainly for, but not limited to AM/FM.
138 */
139 public static final int CONFIG_FORCE_MONO = 1;
140 /**
141 * Forces the analog playback for the supporting radio technology.
142 *
143 * User may disable digital playback for FM HD Radio or hybrid FM/DAB with
144 * this option. This is purely user choice, ie. does not reflect digital-
145 * analog handover state managed from the HAL implementation side.
146 *
147 * Some radio technologies may not support this, ie. DAB.
148 */
149 public static final int CONFIG_FORCE_ANALOG = 2;
150 /**
151 * Forces the digital playback for the supporting radio technology.
152 *
153 * User may disable digital-analog handover that happens with poor
154 * reception conditions. With digital forced, the radio will remain silent
155 * instead of switching to analog channel if it's available. This is purely
156 * user choice, it does not reflect the actual state of handover.
157 */
158 public static final int CONFIG_FORCE_DIGITAL = 3;
159 /**
160 * RDS Alternative Frequencies.
161 *
162 * If set and the currently tuned RDS station broadcasts on multiple
163 * channels, radio tuner automatically switches to the best available
164 * alternative.
165 */
166 public static final int CONFIG_RDS_AF = 4;
167 /**
168 * RDS region-specific program lock-down.
169 *
170 * Allows user to lock to the current region as they move into the
171 * other region.
172 */
173 public static final int CONFIG_RDS_REG = 5;
174 /** Enables DAB-DAB hard- and implicit-linking (the same content). */
175 public static final int CONFIG_DAB_DAB_LINKING = 6;
176 /** Enables DAB-FM hard- and implicit-linking (the same content). */
177 public static final int CONFIG_DAB_FM_LINKING = 7;
178 /** Enables DAB-DAB soft-linking (related content). */
179 public static final int CONFIG_DAB_DAB_SOFT_LINKING = 8;
180 /** Enables DAB-FM soft-linking (related content). */
181 public static final int CONFIG_DAB_FM_SOFT_LINKING = 9;
182
183 /** @hide */
184 @IntDef(prefix = { "CONFIG_" }, value = {
185 CONFIG_FORCE_MONO,
186 CONFIG_FORCE_ANALOG,
187 CONFIG_FORCE_DIGITAL,
188 CONFIG_RDS_AF,
189 CONFIG_RDS_REG,
190 CONFIG_DAB_DAB_LINKING,
191 CONFIG_DAB_FM_LINKING,
192 CONFIG_DAB_DAB_SOFT_LINKING,
193 CONFIG_DAB_FM_SOFT_LINKING,
194 })
195 @Retention(RetentionPolicy.SOURCE)
196 public @interface ConfigFlag {}
197
Eric Laurent2035ac82015-03-05 15:18:44 -0800198 /*****************************************************************************
199 * Lists properties, options and radio bands supported by a given broadcast radio module.
200 * Each module has a unique ID used to address it when calling RadioManager APIs.
201 * Module properties are returned by {@link #listModules(List <ModuleProperties>)} method.
202 ****************************************************************************/
203 public static class ModuleProperties implements Parcelable {
204
205 private final int mId;
Tomasz Wasilczykd15c9df2017-06-13 10:10:36 -0700206 @NonNull private final String mServiceName;
Eric Laurent2035ac82015-03-05 15:18:44 -0800207 private final int mClassId;
208 private final String mImplementor;
209 private final String mProduct;
210 private final String mVersion;
211 private final String mSerial;
212 private final int mNumTuners;
213 private final int mNumAudioSources;
Tomasz Wasilczyk27926ed2018-03-27 13:28:42 -0700214 private final boolean mIsInitializationRequired;
Eric Laurent2035ac82015-03-05 15:18:44 -0800215 private final boolean mIsCaptureSupported;
216 private final BandDescriptor[] mBands;
Tomasz Wasilczyk6849aa82017-03-29 11:59:23 -0700217 private final boolean mIsBgScanSupported;
Tomasz Wasilczyk54587ce2017-07-16 15:15:40 -0700218 private final Set<Integer> mSupportedProgramTypes;
219 private final Set<Integer> mSupportedIdentifierTypes;
Tomasz Wasilczyk58f34062018-01-13 08:25:17 -0800220 @Nullable private final Map<String, Integer> mDabFrequencyTable;
Tomasz Wasilczyk0f1776d2017-08-03 11:03:49 -0700221 @NonNull private final Map<String, String> mVendorInfo;
Eric Laurent2035ac82015-03-05 15:18:44 -0800222
Tomasz Wasilczykd65b3ca2017-12-13 08:26:25 -0800223 /** @hide */
224 public ModuleProperties(int id, String serviceName, int classId, String implementor,
Tomasz Wasilczykd15c9df2017-06-13 10:10:36 -0700225 String product, String version, String serial, int numTuners, int numAudioSources,
Tomasz Wasilczyk27926ed2018-03-27 13:28:42 -0700226 boolean isInitializationRequired, boolean isCaptureSupported,
227 BandDescriptor[] bands, boolean isBgScanSupported,
Tomasz Wasilczyk54587ce2017-07-16 15:15:40 -0700228 @ProgramSelector.ProgramType int[] supportedProgramTypes,
229 @ProgramSelector.IdentifierType int[] supportedIdentifierTypes,
Tomasz Wasilczyk58f34062018-01-13 08:25:17 -0800230 @Nullable Map<String, Integer> dabFrequencyTable,
Tomasz Wasilczyk0f1776d2017-08-03 11:03:49 -0700231 Map<String, String> vendorInfo) {
Eric Laurent2035ac82015-03-05 15:18:44 -0800232 mId = id;
Tomasz Wasilczykd15c9df2017-06-13 10:10:36 -0700233 mServiceName = TextUtils.isEmpty(serviceName) ? "default" : serviceName;
Eric Laurent2035ac82015-03-05 15:18:44 -0800234 mClassId = classId;
235 mImplementor = implementor;
236 mProduct = product;
237 mVersion = version;
238 mSerial = serial;
239 mNumTuners = numTuners;
240 mNumAudioSources = numAudioSources;
Tomasz Wasilczyk27926ed2018-03-27 13:28:42 -0700241 mIsInitializationRequired = isInitializationRequired;
Eric Laurent2035ac82015-03-05 15:18:44 -0800242 mIsCaptureSupported = isCaptureSupported;
243 mBands = bands;
Tomasz Wasilczyk6849aa82017-03-29 11:59:23 -0700244 mIsBgScanSupported = isBgScanSupported;
Tomasz Wasilczyk54587ce2017-07-16 15:15:40 -0700245 mSupportedProgramTypes = arrayToSet(supportedProgramTypes);
246 mSupportedIdentifierTypes = arrayToSet(supportedIdentifierTypes);
Tomasz Wasilczyk58f34062018-01-13 08:25:17 -0800247 if (dabFrequencyTable != null) {
248 for (Map.Entry<String, Integer> entry : dabFrequencyTable.entrySet()) {
249 Objects.requireNonNull(entry.getKey());
250 Objects.requireNonNull(entry.getValue());
251 }
252 }
253 mDabFrequencyTable = dabFrequencyTable;
Tomasz Wasilczyk0f1776d2017-08-03 11:03:49 -0700254 mVendorInfo = (vendorInfo == null) ? new HashMap<>() : vendorInfo;
Eric Laurent2035ac82015-03-05 15:18:44 -0800255 }
256
Tomasz Wasilczyk54587ce2017-07-16 15:15:40 -0700257 private static Set<Integer> arrayToSet(int[] arr) {
258 return Arrays.stream(arr).boxed().collect(Collectors.toSet());
259 }
260
261 private static int[] setToArray(Set<Integer> set) {
262 return set.stream().mapToInt(Integer::intValue).toArray();
263 }
Eric Laurent2035ac82015-03-05 15:18:44 -0800264
265 /** Unique module identifier provided by the native service.
266 * For use with {@link #openTuner(int, BandConfig, boolean, Callback, Handler)}.
267 * @return the radio module unique identifier.
268 */
269 public int getId() {
270 return mId;
271 }
272
Tomasz Wasilczykd15c9df2017-06-13 10:10:36 -0700273 /**
274 * Module service (driver) name as registered with HIDL.
275 * @return the module service name.
Tomasz Wasilczykd15c9df2017-06-13 10:10:36 -0700276 */
277 public @NonNull String getServiceName() {
278 return mServiceName;
279 }
280
Eric Laurent2035ac82015-03-05 15:18:44 -0800281 /** Module class identifier: {@link #CLASS_AM_FM}, {@link #CLASS_SAT}, {@link #CLASS_DT}
282 * @return the radio module class identifier.
283 */
284 public int getClassId() {
285 return mClassId;
286 }
287
288 /** Human readable broadcast radio module implementor
289 * @return the name of the radio module implementator.
290 */
291 public String getImplementor() {
292 return mImplementor;
293 }
294
295 /** Human readable broadcast radio module product name
296 * @return the radio module product name.
297 */
298 public String getProduct() {
299 return mProduct;
300 }
301
302 /** Human readable broadcast radio module version number
303 * @return the radio module version.
304 */
305 public String getVersion() {
306 return mVersion;
307 }
308
309 /** Radio module serial number.
310 * Can be used for subscription services.
311 * @return the radio module serial number.
312 */
313 public String getSerial() {
314 return mSerial;
315 }
316
317 /** Number of tuners available.
318 * This is the number of tuners that can be open simultaneously.
319 * @return the number of tuners supported.
320 */
321 public int getNumTuners() {
322 return mNumTuners;
323 }
324
325 /** Number tuner audio sources available. Must be less or equal to getNumTuners().
326 * When more than one tuner is supported, one is usually for playback and has one
327 * associated audio source and the other is for pre scanning and building a
328 * program list.
329 * @return the number of audio sources available.
330 */
331 public int getNumAudioSources() {
332 return mNumAudioSources;
333 }
334
Tomasz Wasilczyk27926ed2018-03-27 13:28:42 -0700335 /**
336 * Checks, if BandConfig initialization (after {@link RadioManager#openTuner})
337 * is required to be done before other operations or not.
338 *
339 * If it is, the client has to wait for {@link RadioTuner.Callback#onConfigurationChanged}
340 * callback before executing any other operations. Otherwise, such operation will fail
341 * returning {@link RadioManager#STATUS_INVALID_OPERATION} error code.
342 */
343 public boolean isInitializationRequired() {
344 return mIsInitializationRequired;
345 }
346
Eric Laurent2035ac82015-03-05 15:18:44 -0800347 /** {@code true} if audio capture is possible from radio tuner output.
348 * This indicates if routing to audio devices not connected to the same HAL as the FM radio
349 * is possible (e.g. to USB) or DAR (Digital Audio Recorder) feature can be implemented.
350 * @return {@code true} if audio capture is possible, {@code false} otherwise.
351 */
352 public boolean isCaptureSupported() {
353 return mIsCaptureSupported;
354 }
355
Tomasz Wasilczyke597ce12017-03-24 13:50:53 -0700356 /**
357 * {@code true} if the module supports background scanning. At the given time it may not
358 * be available though, see {@link RadioTuner#startBackgroundScan()}.
359 *
360 * @return {@code true} if background scanning is supported (not necessary available
361 * at a given time), {@code false} otherwise.
Tomasz Wasilczyke597ce12017-03-24 13:50:53 -0700362 */
363 public boolean isBackgroundScanningSupported() {
Tomasz Wasilczyk6849aa82017-03-29 11:59:23 -0700364 return mIsBgScanSupported;
365 }
366
367 /**
Tomasz Wasilczyk54587ce2017-07-16 15:15:40 -0700368 * Checks, if a given program type is supported by this tuner.
369 *
370 * If a program type is supported by radio module, it means it can tune
371 * to ProgramSelector of a given type.
372 *
373 * @return {@code true} if a given program type is supported.
374 */
375 public boolean isProgramTypeSupported(@ProgramSelector.ProgramType int type) {
376 return mSupportedProgramTypes.contains(type);
377 }
378
379 /**
380 * Checks, if a given program identifier is supported by this tuner.
381 *
382 * If an identifier is supported by radio module, it means it can use it for
383 * tuning to ProgramSelector with either primary or secondary Identifier of
384 * a given type.
385 *
386 * @return {@code true} if a given program type is supported.
387 */
388 public boolean isProgramIdentifierSupported(@ProgramSelector.IdentifierType int type) {
389 return mSupportedIdentifierTypes.contains(type);
390 }
391
392 /**
Tomasz Wasilczyk58f34062018-01-13 08:25:17 -0800393 * A frequency table for Digital Audio Broadcasting (DAB).
394 *
395 * The key is a channel name, i.e. 5A, 7B.
396 *
397 * The value is a frequency, in kHz.
398 *
399 * @return a frequency table, or {@code null} if the module doesn't support DAB
400 */
401 public @Nullable Map<String, Integer> getDabFrequencyTable() {
402 return mDabFrequencyTable;
403 }
404
405 /**
Tomasz Wasilczyk0f1776d2017-08-03 11:03:49 -0700406 * A map of vendor-specific opaque strings, passed from HAL without changes.
407 * Format of these strings can vary across vendors.
Tomasz Wasilczyk6849aa82017-03-29 11:59:23 -0700408 *
409 * It may be used for extra features, that's not supported by a platform,
Tomasz Wasilczyk0f1776d2017-08-03 11:03:49 -0700410 * for example: preset-slots=6; ultra-hd-capable=false.
Tomasz Wasilczyk6849aa82017-03-29 11:59:23 -0700411 *
Tomasz Wasilczyk0f1776d2017-08-03 11:03:49 -0700412 * Keys must be prefixed with unique vendor Java-style namespace,
413 * eg. 'com.somecompany.parameter1'.
Tomasz Wasilczyk6849aa82017-03-29 11:59:23 -0700414 */
Tomasz Wasilczyk0f1776d2017-08-03 11:03:49 -0700415 public @NonNull Map<String, String> getVendorInfo() {
416 return mVendorInfo;
Tomasz Wasilczyke597ce12017-03-24 13:50:53 -0700417 }
418
Eric Laurent2035ac82015-03-05 15:18:44 -0800419 /** List of descriptors for all bands supported by this module.
420 * @return an array of {@link BandDescriptor}.
421 */
422 public BandDescriptor[] getBands() {
423 return mBands;
424 }
425
426 private ModuleProperties(Parcel in) {
427 mId = in.readInt();
Tomasz Wasilczykd15c9df2017-06-13 10:10:36 -0700428 String serviceName = in.readString();
429 mServiceName = TextUtils.isEmpty(serviceName) ? "default" : serviceName;
Eric Laurent2035ac82015-03-05 15:18:44 -0800430 mClassId = in.readInt();
431 mImplementor = in.readString();
432 mProduct = in.readString();
433 mVersion = in.readString();
434 mSerial = in.readString();
435 mNumTuners = in.readInt();
436 mNumAudioSources = in.readInt();
Tomasz Wasilczyk27926ed2018-03-27 13:28:42 -0700437 mIsInitializationRequired = in.readInt() == 1;
Eric Laurent2035ac82015-03-05 15:18:44 -0800438 mIsCaptureSupported = in.readInt() == 1;
439 Parcelable[] tmp = in.readParcelableArray(BandDescriptor.class.getClassLoader());
440 mBands = new BandDescriptor[tmp.length];
441 for (int i = 0; i < tmp.length; i++) {
442 mBands[i] = (BandDescriptor) tmp[i];
443 }
Tomasz Wasilczyk6849aa82017-03-29 11:59:23 -0700444 mIsBgScanSupported = in.readInt() == 1;
Tomasz Wasilczyk54587ce2017-07-16 15:15:40 -0700445 mSupportedProgramTypes = arrayToSet(in.createIntArray());
446 mSupportedIdentifierTypes = arrayToSet(in.createIntArray());
Tomasz Wasilczyk58f34062018-01-13 08:25:17 -0800447 mDabFrequencyTable = Utils.readStringIntMap(in);
Tomasz Wasilczyk436128f2018-01-08 16:46:09 -0800448 mVendorInfo = Utils.readStringMap(in);
Eric Laurent2035ac82015-03-05 15:18:44 -0800449 }
450
Jeff Sharkey9e8f83d2019-02-28 12:06:45 -0700451 public static final @android.annotation.NonNull Parcelable.Creator<ModuleProperties> CREATOR
Eric Laurent2035ac82015-03-05 15:18:44 -0800452 = new Parcelable.Creator<ModuleProperties>() {
453 public ModuleProperties createFromParcel(Parcel in) {
454 return new ModuleProperties(in);
455 }
456
457 public ModuleProperties[] newArray(int size) {
458 return new ModuleProperties[size];
459 }
460 };
461
462 @Override
463 public void writeToParcel(Parcel dest, int flags) {
464 dest.writeInt(mId);
Tomasz Wasilczykd15c9df2017-06-13 10:10:36 -0700465 dest.writeString(mServiceName);
Eric Laurent2035ac82015-03-05 15:18:44 -0800466 dest.writeInt(mClassId);
467 dest.writeString(mImplementor);
468 dest.writeString(mProduct);
469 dest.writeString(mVersion);
470 dest.writeString(mSerial);
471 dest.writeInt(mNumTuners);
472 dest.writeInt(mNumAudioSources);
Tomasz Wasilczyk27926ed2018-03-27 13:28:42 -0700473 dest.writeInt(mIsInitializationRequired ? 1 : 0);
Eric Laurent2035ac82015-03-05 15:18:44 -0800474 dest.writeInt(mIsCaptureSupported ? 1 : 0);
475 dest.writeParcelableArray(mBands, flags);
Tomasz Wasilczyk6849aa82017-03-29 11:59:23 -0700476 dest.writeInt(mIsBgScanSupported ? 1 : 0);
Tomasz Wasilczyk54587ce2017-07-16 15:15:40 -0700477 dest.writeIntArray(setToArray(mSupportedProgramTypes));
478 dest.writeIntArray(setToArray(mSupportedIdentifierTypes));
Tomasz Wasilczyk58f34062018-01-13 08:25:17 -0800479 Utils.writeStringIntMap(dest, mDabFrequencyTable);
Tomasz Wasilczyk436128f2018-01-08 16:46:09 -0800480 Utils.writeStringMap(dest, mVendorInfo);
Eric Laurent2035ac82015-03-05 15:18:44 -0800481 }
482
483 @Override
484 public int describeContents() {
485 return 0;
486 }
487
488 @Override
489 public String toString() {
Tomasz Wasilczykd15c9df2017-06-13 10:10:36 -0700490 return "ModuleProperties [mId=" + mId
491 + ", mServiceName=" + mServiceName + ", mClassId=" + mClassId
Eric Laurent2035ac82015-03-05 15:18:44 -0800492 + ", mImplementor=" + mImplementor + ", mProduct=" + mProduct
493 + ", mVersion=" + mVersion + ", mSerial=" + mSerial
494 + ", mNumTuners=" + mNumTuners
495 + ", mNumAudioSources=" + mNumAudioSources
Tomasz Wasilczyk27926ed2018-03-27 13:28:42 -0700496 + ", mIsInitializationRequired=" + mIsInitializationRequired
Eric Laurent2035ac82015-03-05 15:18:44 -0800497 + ", mIsCaptureSupported=" + mIsCaptureSupported
Tomasz Wasilczyk6849aa82017-03-29 11:59:23 -0700498 + ", mIsBgScanSupported=" + mIsBgScanSupported
Eric Laurent2035ac82015-03-05 15:18:44 -0800499 + ", mBands=" + Arrays.toString(mBands) + "]";
500 }
501
502 @Override
503 public int hashCode() {
Tomasz Wasilczyk58f34062018-01-13 08:25:17 -0800504 return Objects.hash(mId, mServiceName, mClassId, mImplementor, mProduct, mVersion,
Tomasz Wasilczyk27926ed2018-03-27 13:28:42 -0700505 mSerial, mNumTuners, mNumAudioSources, mIsInitializationRequired,
506 mIsCaptureSupported, mBands, mIsBgScanSupported, mDabFrequencyTable, mVendorInfo);
Eric Laurent2035ac82015-03-05 15:18:44 -0800507 }
508
509 @Override
510 public boolean equals(Object obj) {
Tomasz Wasilczyk58f34062018-01-13 08:25:17 -0800511 if (this == obj) return true;
512 if (!(obj instanceof ModuleProperties)) return false;
Eric Laurent2035ac82015-03-05 15:18:44 -0800513 ModuleProperties other = (ModuleProperties) obj;
Tomasz Wasilczyk58f34062018-01-13 08:25:17 -0800514
515 if (mId != other.getId()) return false;
Tomasz Wasilczykd15c9df2017-06-13 10:10:36 -0700516 if (!TextUtils.equals(mServiceName, other.mServiceName)) return false;
Tomasz Wasilczyk58f34062018-01-13 08:25:17 -0800517 if (mClassId != other.mClassId) return false;
518 if (!Objects.equals(mImplementor, other.mImplementor)) return false;
519 if (!Objects.equals(mProduct, other.mProduct)) return false;
520 if (!Objects.equals(mVersion, other.mVersion)) return false;
521 if (!Objects.equals(mSerial, other.mSerial)) return false;
522 if (mNumTuners != other.mNumTuners) return false;
523 if (mNumAudioSources != other.mNumAudioSources) return false;
Tomasz Wasilczyk27926ed2018-03-27 13:28:42 -0700524 if (mIsInitializationRequired != other.mIsInitializationRequired) return false;
Tomasz Wasilczyk58f34062018-01-13 08:25:17 -0800525 if (mIsCaptureSupported != other.mIsCaptureSupported) return false;
526 if (!Objects.equals(mBands, other.mBands)) return false;
527 if (mIsBgScanSupported != other.mIsBgScanSupported) return false;
528 if (!Objects.equals(mDabFrequencyTable, other.mDabFrequencyTable)) return false;
529 if (!Objects.equals(mVendorInfo, other.mVendorInfo)) return false;
Eric Laurent2035ac82015-03-05 15:18:44 -0800530 return true;
531 }
532 }
533
534 /** Radio band descriptor: an element in ModuleProperties bands array.
535 * It is either an instance of {@link FmBandDescriptor} or {@link AmBandDescriptor} */
536 public static class BandDescriptor implements Parcelable {
537
538 private final int mRegion;
539 private final int mType;
540 private final int mLowerLimit;
541 private final int mUpperLimit;
542 private final int mSpacing;
543
544 BandDescriptor(int region, int type, int lowerLimit, int upperLimit, int spacing) {
Tomasz Wasilczykf24ecf72017-04-24 13:32:32 -0700545 if (type != BAND_AM && type != BAND_FM && type != BAND_FM_HD && type != BAND_AM_HD) {
546 throw new IllegalArgumentException("Unsupported band: " + type);
547 }
Eric Laurent2035ac82015-03-05 15:18:44 -0800548 mRegion = region;
549 mType = type;
550 mLowerLimit = lowerLimit;
551 mUpperLimit = upperLimit;
552 mSpacing = spacing;
553 }
554
555 /** Region this band applies to. E.g. {@link #REGION_ITU_1}
556 * @return the region this band is associated to.
557 */
558 public int getRegion() {
559 return mRegion;
560 }
561 /** Band type, e.g {@link #BAND_FM}. Defines the subclass this descriptor can be cast to:
562 * <ul>
563 * <li>{@link #BAND_FM} or {@link #BAND_FM_HD} cast to {@link FmBandDescriptor}, </li>
564 * <li>{@link #BAND_AM} cast to {@link AmBandDescriptor}, </li>
565 * </ul>
566 * @return the band type.
567 */
568 public int getType() {
569 return mType;
570 }
Tomasz Wasilczyk2880b9a2017-06-28 14:28:47 -0700571
572 /**
573 * Checks if the band is either AM or AM_HD.
574 *
575 * @return {@code true}, if band is AM or AM_HD.
576 */
577 public boolean isAmBand() {
578 return mType == BAND_AM || mType == BAND_AM_HD;
579 }
580
581 /**
582 * Checks if the band is either FM or FM_HD.
583 *
584 * @return {@code true}, if band is FM or FM_HD.
585 */
586 public boolean isFmBand() {
587 return mType == BAND_FM || mType == BAND_FM_HD;
588 }
589
Eric Laurent2035ac82015-03-05 15:18:44 -0800590 /** Lower band limit expressed in units according to band type.
591 * Currently all defined band types express channels as frequency in kHz
592 * @return the lower band limit.
593 */
594 public int getLowerLimit() {
595 return mLowerLimit;
596 }
597 /** Upper band limit expressed in units according to band type.
598 * Currently all defined band types express channels as frequency in kHz
599 * @return the upper band limit.
600 */
601 public int getUpperLimit() {
602 return mUpperLimit;
603 }
604 /** Channel spacing in units according to band type.
605 * Currently all defined band types express channels as frequency in kHz
606 * @return the channel spacing.
607 */
608 public int getSpacing() {
609 return mSpacing;
610 }
611
612 private BandDescriptor(Parcel in) {
613 mRegion = in.readInt();
614 mType = in.readInt();
615 mLowerLimit = in.readInt();
616 mUpperLimit = in.readInt();
617 mSpacing = in.readInt();
618 }
619
Tomasz Wasilczykf24ecf72017-04-24 13:32:32 -0700620 private static int lookupTypeFromParcel(Parcel in) {
621 int pos = in.dataPosition();
622 in.readInt(); // skip region
623 int type = in.readInt();
624 in.setDataPosition(pos);
625 return type;
626 }
627
Jeff Sharkey9e8f83d2019-02-28 12:06:45 -0700628 public static final @android.annotation.NonNull Parcelable.Creator<BandDescriptor> CREATOR
Eric Laurent2035ac82015-03-05 15:18:44 -0800629 = new Parcelable.Creator<BandDescriptor>() {
630 public BandDescriptor createFromParcel(Parcel in) {
Tomasz Wasilczykf24ecf72017-04-24 13:32:32 -0700631 int type = lookupTypeFromParcel(in);
632 switch (type) {
633 case BAND_FM:
634 case BAND_FM_HD:
635 return new FmBandDescriptor(in);
636 case BAND_AM:
637 case BAND_AM_HD:
638 return new AmBandDescriptor(in);
639 default:
640 throw new IllegalArgumentException("Unsupported band: " + type);
641 }
Eric Laurent2035ac82015-03-05 15:18:44 -0800642 }
643
644 public BandDescriptor[] newArray(int size) {
645 return new BandDescriptor[size];
646 }
647 };
648
649 @Override
650 public void writeToParcel(Parcel dest, int flags) {
651 dest.writeInt(mRegion);
652 dest.writeInt(mType);
653 dest.writeInt(mLowerLimit);
654 dest.writeInt(mUpperLimit);
655 dest.writeInt(mSpacing);
656 }
657
658 @Override
659 public int describeContents() {
660 return 0;
661 }
662
663 @Override
664 public String toString() {
665 return "BandDescriptor [mRegion=" + mRegion + ", mType=" + mType + ", mLowerLimit="
666 + mLowerLimit + ", mUpperLimit=" + mUpperLimit + ", mSpacing=" + mSpacing + "]";
667 }
668
669 @Override
670 public int hashCode() {
671 final int prime = 31;
672 int result = 1;
673 result = prime * result + mRegion;
674 result = prime * result + mType;
675 result = prime * result + mLowerLimit;
676 result = prime * result + mUpperLimit;
677 result = prime * result + mSpacing;
678 return result;
679 }
680
681 @Override
682 public boolean equals(Object obj) {
683 if (this == obj)
684 return true;
685 if (!(obj instanceof BandDescriptor))
686 return false;
687 BandDescriptor other = (BandDescriptor) obj;
688 if (mRegion != other.getRegion())
689 return false;
690 if (mType != other.getType())
691 return false;
692 if (mLowerLimit != other.getLowerLimit())
693 return false;
694 if (mUpperLimit != other.getUpperLimit())
695 return false;
696 if (mSpacing != other.getSpacing())
697 return false;
698 return true;
699 }
700 }
701
702 /** FM band descriptor
703 * @see #BAND_FM
704 * @see #BAND_FM_HD */
705 public static class FmBandDescriptor extends BandDescriptor {
706 private final boolean mStereo;
707 private final boolean mRds;
708 private final boolean mTa;
709 private final boolean mAf;
Sanket Agarwal7058e4c2015-10-08 14:14:20 -0700710 private final boolean mEa;
Eric Laurent2035ac82015-03-05 15:18:44 -0800711
Tomasz Wasilczykf58305d2017-12-28 14:03:15 -0800712 /** @hide */
713 public FmBandDescriptor(int region, int type, int lowerLimit, int upperLimit, int spacing,
Sanket Agarwal7058e4c2015-10-08 14:14:20 -0700714 boolean stereo, boolean rds, boolean ta, boolean af, boolean ea) {
Eric Laurent2035ac82015-03-05 15:18:44 -0800715 super(region, type, lowerLimit, upperLimit, spacing);
716 mStereo = stereo;
717 mRds = rds;
718 mTa = ta;
719 mAf = af;
Sanket Agarwal7058e4c2015-10-08 14:14:20 -0700720 mEa = ea;
Eric Laurent2035ac82015-03-05 15:18:44 -0800721 }
722
723 /** Stereo is supported
724 * @return {@code true} if stereo is supported, {@code false} otherwise.
725 */
726 public boolean isStereoSupported() {
727 return mStereo;
728 }
729 /** RDS or RBDS(if region is ITU2) is supported
730 * @return {@code true} if RDS or RBDS is supported, {@code false} otherwise.
731 */
732 public boolean isRdsSupported() {
733 return mRds;
734 }
735 /** Traffic announcement is supported
736 * @return {@code true} if TA is supported, {@code false} otherwise.
737 */
738 public boolean isTaSupported() {
739 return mTa;
740 }
741 /** Alternate Frequency Switching is supported
742 * @return {@code true} if AF switching is supported, {@code false} otherwise.
743 */
744 public boolean isAfSupported() {
745 return mAf;
746 }
747
Sanket Agarwal7058e4c2015-10-08 14:14:20 -0700748 /** Emergency Announcement is supported
749 * @return {@code true} if Emergency annoucement is supported, {@code false} otherwise.
750 */
751 public boolean isEaSupported() {
752 return mEa;
753 }
754
Eric Laurent2035ac82015-03-05 15:18:44 -0800755 /* Parcelable implementation */
756 private FmBandDescriptor(Parcel in) {
757 super(in);
758 mStereo = in.readByte() == 1;
759 mRds = in.readByte() == 1;
760 mTa = in.readByte() == 1;
761 mAf = in.readByte() == 1;
Sanket Agarwal7058e4c2015-10-08 14:14:20 -0700762 mEa = in.readByte() == 1;
Eric Laurent2035ac82015-03-05 15:18:44 -0800763 }
764
Jeff Sharkey9e8f83d2019-02-28 12:06:45 -0700765 public static final @android.annotation.NonNull Parcelable.Creator<FmBandDescriptor> CREATOR
Eric Laurent2035ac82015-03-05 15:18:44 -0800766 = new Parcelable.Creator<FmBandDescriptor>() {
767 public FmBandDescriptor createFromParcel(Parcel in) {
768 return new FmBandDescriptor(in);
769 }
770
771 public FmBandDescriptor[] newArray(int size) {
772 return new FmBandDescriptor[size];
773 }
774 };
775
776 @Override
777 public void writeToParcel(Parcel dest, int flags) {
778 super.writeToParcel(dest, flags);
779 dest.writeByte((byte) (mStereo ? 1 : 0));
780 dest.writeByte((byte) (mRds ? 1 : 0));
781 dest.writeByte((byte) (mTa ? 1 : 0));
782 dest.writeByte((byte) (mAf ? 1 : 0));
Sanket Agarwal7058e4c2015-10-08 14:14:20 -0700783 dest.writeByte((byte) (mEa ? 1 : 0));
Eric Laurent2035ac82015-03-05 15:18:44 -0800784 }
785
786 @Override
787 public int describeContents() {
788 return 0;
789 }
790
791 @Override
792 public String toString() {
793 return "FmBandDescriptor [ "+ super.toString() + " mStereo=" + mStereo
Sanket Agarwal7058e4c2015-10-08 14:14:20 -0700794 + ", mRds=" + mRds + ", mTa=" + mTa + ", mAf=" + mAf +
795 ", mEa =" + mEa + "]";
Eric Laurent2035ac82015-03-05 15:18:44 -0800796 }
797
798 @Override
799 public int hashCode() {
800 final int prime = 31;
801 int result = super.hashCode();
802 result = prime * result + (mStereo ? 1 : 0);
803 result = prime * result + (mRds ? 1 : 0);
804 result = prime * result + (mTa ? 1 : 0);
805 result = prime * result + (mAf ? 1 : 0);
Sanket Agarwal7058e4c2015-10-08 14:14:20 -0700806 result = prime * result + (mEa ? 1 : 0);
Eric Laurent2035ac82015-03-05 15:18:44 -0800807 return result;
808 }
809
810 @Override
811 public boolean equals(Object obj) {
812 if (this == obj)
813 return true;
814 if (!super.equals(obj))
815 return false;
816 if (!(obj instanceof FmBandDescriptor))
817 return false;
818 FmBandDescriptor other = (FmBandDescriptor) obj;
819 if (mStereo != other.isStereoSupported())
820 return false;
821 if (mRds != other.isRdsSupported())
822 return false;
823 if (mTa != other.isTaSupported())
824 return false;
825 if (mAf != other.isAfSupported())
826 return false;
Sanket Agarwal7058e4c2015-10-08 14:14:20 -0700827 if (mEa != other.isEaSupported())
828 return false;
Eric Laurent2035ac82015-03-05 15:18:44 -0800829 return true;
830 }
831 }
832
833 /** AM band descriptor.
834 * @see #BAND_AM */
835 public static class AmBandDescriptor extends BandDescriptor {
836
837 private final boolean mStereo;
838
Tomasz Wasilczykf58305d2017-12-28 14:03:15 -0800839 /** @hide */
840 public AmBandDescriptor(int region, int type, int lowerLimit, int upperLimit, int spacing,
Eric Laurent2035ac82015-03-05 15:18:44 -0800841 boolean stereo) {
842 super(region, type, lowerLimit, upperLimit, spacing);
843 mStereo = stereo;
844 }
845
846 /** Stereo is supported
847 * @return {@code true} if stereo is supported, {@code false} otherwise.
848 */
849 public boolean isStereoSupported() {
850 return mStereo;
851 }
852
853 private AmBandDescriptor(Parcel in) {
854 super(in);
855 mStereo = in.readByte() == 1;
856 }
857
Jeff Sharkey9e8f83d2019-02-28 12:06:45 -0700858 public static final @android.annotation.NonNull Parcelable.Creator<AmBandDescriptor> CREATOR
Eric Laurent2035ac82015-03-05 15:18:44 -0800859 = new Parcelable.Creator<AmBandDescriptor>() {
860 public AmBandDescriptor createFromParcel(Parcel in) {
861 return new AmBandDescriptor(in);
862 }
863
864 public AmBandDescriptor[] newArray(int size) {
865 return new AmBandDescriptor[size];
866 }
867 };
868
869 @Override
870 public void writeToParcel(Parcel dest, int flags) {
871 super.writeToParcel(dest, flags);
872 dest.writeByte((byte) (mStereo ? 1 : 0));
873 }
874
875 @Override
876 public int describeContents() {
877 return 0;
878 }
879
880 @Override
881 public String toString() {
882 return "AmBandDescriptor [ "+ super.toString() + " mStereo=" + mStereo + "]";
883 }
884
885 @Override
886 public int hashCode() {
887 final int prime = 31;
888 int result = super.hashCode();
889 result = prime * result + (mStereo ? 1 : 0);
890 return result;
891 }
892
893 @Override
894 public boolean equals(Object obj) {
895 if (this == obj)
896 return true;
897 if (!super.equals(obj))
898 return false;
899 if (!(obj instanceof AmBandDescriptor))
900 return false;
901 AmBandDescriptor other = (AmBandDescriptor) obj;
902 if (mStereo != other.isStereoSupported())
903 return false;
904 return true;
905 }
906 }
907
908
909 /** Radio band configuration. */
910 public static class BandConfig implements Parcelable {
911
Tomasz Wasilczykf58305d2017-12-28 14:03:15 -0800912 @NonNull final BandDescriptor mDescriptor;
Eric Laurent2035ac82015-03-05 15:18:44 -0800913
914 BandConfig(BandDescriptor descriptor) {
Tomasz Wasilczykf58305d2017-12-28 14:03:15 -0800915 mDescriptor = Objects.requireNonNull(descriptor);
Eric Laurent2035ac82015-03-05 15:18:44 -0800916 }
917
918 BandConfig(int region, int type, int lowerLimit, int upperLimit, int spacing) {
919 mDescriptor = new BandDescriptor(region, type, lowerLimit, upperLimit, spacing);
920 }
921
922 private BandConfig(Parcel in) {
923 mDescriptor = new BandDescriptor(in);
924 }
925
926 BandDescriptor getDescriptor() {
927 return mDescriptor;
928 }
929
930 /** Region this band applies to. E.g. {@link #REGION_ITU_1}
931 * @return the region associated with this band.
932 */
933 public int getRegion() {
934 return mDescriptor.getRegion();
935 }
936 /** Band type, e.g {@link #BAND_FM}. Defines the subclass this descriptor can be cast to:
937 * <ul>
938 * <li>{@link #BAND_FM} or {@link #BAND_FM_HD} cast to {@link FmBandDescriptor}, </li>
939 * <li>{@link #BAND_AM} cast to {@link AmBandDescriptor}, </li>
940 * </ul>
941 * @return the band type.
942 */
943 public int getType() {
944 return mDescriptor.getType();
945 }
946 /** Lower band limit expressed in units according to band type.
947 * Currently all defined band types express channels as frequency in kHz
948 * @return the lower band limit.
949 */
950 public int getLowerLimit() {
951 return mDescriptor.getLowerLimit();
952 }
953 /** Upper band limit expressed in units according to band type.
954 * Currently all defined band types express channels as frequency in kHz
955 * @return the upper band limit.
956 */
957 public int getUpperLimit() {
958 return mDescriptor.getUpperLimit();
959 }
960 /** Channel spacing in units according to band type.
961 * Currently all defined band types express channels as frequency in kHz
962 * @return the channel spacing.
963 */
964 public int getSpacing() {
965 return mDescriptor.getSpacing();
966 }
967
968
Jeff Sharkey9e8f83d2019-02-28 12:06:45 -0700969 public static final @android.annotation.NonNull Parcelable.Creator<BandConfig> CREATOR
Eric Laurent2035ac82015-03-05 15:18:44 -0800970 = new Parcelable.Creator<BandConfig>() {
971 public BandConfig createFromParcel(Parcel in) {
Tomasz Wasilczykf24ecf72017-04-24 13:32:32 -0700972 int type = BandDescriptor.lookupTypeFromParcel(in);
973 switch (type) {
974 case BAND_FM:
975 case BAND_FM_HD:
976 return new FmBandConfig(in);
977 case BAND_AM:
978 case BAND_AM_HD:
979 return new AmBandConfig(in);
980 default:
981 throw new IllegalArgumentException("Unsupported band: " + type);
982 }
Eric Laurent2035ac82015-03-05 15:18:44 -0800983 }
984
985 public BandConfig[] newArray(int size) {
986 return new BandConfig[size];
987 }
988 };
989
990 @Override
991 public void writeToParcel(Parcel dest, int flags) {
992 mDescriptor.writeToParcel(dest, flags);
993 }
994
995 @Override
996 public int describeContents() {
997 return 0;
998 }
999
1000 @Override
1001 public String toString() {
1002 return "BandConfig [ " + mDescriptor.toString() + "]";
1003 }
1004
1005 @Override
1006 public int hashCode() {
1007 final int prime = 31;
1008 int result = 1;
1009 result = prime * result + mDescriptor.hashCode();
1010 return result;
1011 }
1012
1013 @Override
1014 public boolean equals(Object obj) {
1015 if (this == obj)
1016 return true;
1017 if (!(obj instanceof BandConfig))
1018 return false;
1019 BandConfig other = (BandConfig) obj;
Tomasz Wasilczykdd767062017-04-28 08:19:34 -07001020 BandDescriptor otherDesc = other.getDescriptor();
1021 if ((mDescriptor == null) != (otherDesc == null)) return false;
1022 if (mDescriptor != null && !mDescriptor.equals(otherDesc)) return false;
Eric Laurent2035ac82015-03-05 15:18:44 -08001023 return true;
1024 }
1025 }
1026
1027 /** FM band configuration.
1028 * @see #BAND_FM
1029 * @see #BAND_FM_HD */
1030 public static class FmBandConfig extends BandConfig {
1031 private final boolean mStereo;
1032 private final boolean mRds;
1033 private final boolean mTa;
1034 private final boolean mAf;
Sanket Agarwal7058e4c2015-10-08 14:14:20 -07001035 private final boolean mEa;
Eric Laurent2035ac82015-03-05 15:18:44 -08001036
Tomasz Wasilczykf58305d2017-12-28 14:03:15 -08001037 /** @hide */
1038 public FmBandConfig(FmBandDescriptor descriptor) {
Eric Laurent2035ac82015-03-05 15:18:44 -08001039 super((BandDescriptor)descriptor);
1040 mStereo = descriptor.isStereoSupported();
1041 mRds = descriptor.isRdsSupported();
1042 mTa = descriptor.isTaSupported();
1043 mAf = descriptor.isAfSupported();
Sanket Agarwal7058e4c2015-10-08 14:14:20 -07001044 mEa = descriptor.isEaSupported();
Eric Laurent2035ac82015-03-05 15:18:44 -08001045 }
1046
1047 FmBandConfig(int region, int type, int lowerLimit, int upperLimit, int spacing,
Sanket Agarwal7058e4c2015-10-08 14:14:20 -07001048 boolean stereo, boolean rds, boolean ta, boolean af, boolean ea) {
Eric Laurent2035ac82015-03-05 15:18:44 -08001049 super(region, type, lowerLimit, upperLimit, spacing);
1050 mStereo = stereo;
1051 mRds = rds;
1052 mTa = ta;
1053 mAf = af;
Sanket Agarwal7058e4c2015-10-08 14:14:20 -07001054 mEa = ea;
Eric Laurent2035ac82015-03-05 15:18:44 -08001055 }
1056
1057 /** Get stereo enable state
1058 * @return the enable state.
1059 */
1060 public boolean getStereo() {
1061 return mStereo;
1062 }
1063
1064 /** Get RDS or RBDS(if region is ITU2) enable state
1065 * @return the enable state.
1066 */
1067 public boolean getRds() {
1068 return mRds;
1069 }
1070
1071 /** Get Traffic announcement enable state
1072 * @return the enable state.
1073 */
1074 public boolean getTa() {
1075 return mTa;
1076 }
1077
1078 /** Get Alternate Frequency Switching enable state
1079 * @return the enable state.
1080 */
1081 public boolean getAf() {
1082 return mAf;
1083 }
1084
Sanket Agarwal7058e4c2015-10-08 14:14:20 -07001085 /**
1086 * Get Emergency announcement enable state
1087 * @return the enable state.
1088 */
1089 public boolean getEa() {
1090 return mEa;
1091 }
1092
Eric Laurent2035ac82015-03-05 15:18:44 -08001093 private FmBandConfig(Parcel in) {
1094 super(in);
1095 mStereo = in.readByte() == 1;
1096 mRds = in.readByte() == 1;
1097 mTa = in.readByte() == 1;
1098 mAf = in.readByte() == 1;
Sanket Agarwal7058e4c2015-10-08 14:14:20 -07001099 mEa = in.readByte() == 1;
Eric Laurent2035ac82015-03-05 15:18:44 -08001100 }
1101
Jeff Sharkey9e8f83d2019-02-28 12:06:45 -07001102 public static final @android.annotation.NonNull Parcelable.Creator<FmBandConfig> CREATOR
Eric Laurent2035ac82015-03-05 15:18:44 -08001103 = new Parcelable.Creator<FmBandConfig>() {
1104 public FmBandConfig createFromParcel(Parcel in) {
1105 return new FmBandConfig(in);
1106 }
1107
1108 public FmBandConfig[] newArray(int size) {
1109 return new FmBandConfig[size];
1110 }
1111 };
1112
1113 @Override
1114 public void writeToParcel(Parcel dest, int flags) {
1115 super.writeToParcel(dest, flags);
1116 dest.writeByte((byte) (mStereo ? 1 : 0));
1117 dest.writeByte((byte) (mRds ? 1 : 0));
1118 dest.writeByte((byte) (mTa ? 1 : 0));
1119 dest.writeByte((byte) (mAf ? 1 : 0));
Sanket Agarwal7058e4c2015-10-08 14:14:20 -07001120 dest.writeByte((byte) (mEa ? 1 : 0));
Eric Laurent2035ac82015-03-05 15:18:44 -08001121 }
1122
1123 @Override
1124 public int describeContents() {
1125 return 0;
1126 }
1127
1128 @Override
1129 public String toString() {
1130 return "FmBandConfig [" + super.toString()
1131 + ", mStereo=" + mStereo + ", mRds=" + mRds + ", mTa=" + mTa
Sanket Agarwal7058e4c2015-10-08 14:14:20 -07001132 + ", mAf=" + mAf + ", mEa =" + mEa + "]";
Eric Laurent2035ac82015-03-05 15:18:44 -08001133 }
1134
1135 @Override
1136 public int hashCode() {
1137 final int prime = 31;
1138 int result = super.hashCode();
1139 result = prime * result + (mStereo ? 1 : 0);
1140 result = prime * result + (mRds ? 1 : 0);
1141 result = prime * result + (mTa ? 1 : 0);
1142 result = prime * result + (mAf ? 1 : 0);
Sanket Agarwal7058e4c2015-10-08 14:14:20 -07001143 result = prime * result + (mEa ? 1 : 0);
Eric Laurent2035ac82015-03-05 15:18:44 -08001144 return result;
1145 }
1146
1147 @Override
1148 public boolean equals(Object obj) {
1149 if (this == obj)
1150 return true;
1151 if (!super.equals(obj))
1152 return false;
1153 if (!(obj instanceof FmBandConfig))
1154 return false;
1155 FmBandConfig other = (FmBandConfig) obj;
1156 if (mStereo != other.mStereo)
1157 return false;
1158 if (mRds != other.mRds)
1159 return false;
1160 if (mTa != other.mTa)
1161 return false;
1162 if (mAf != other.mAf)
1163 return false;
Sanket Agarwal7058e4c2015-10-08 14:14:20 -07001164 if (mEa != other.mEa)
1165 return false;
Eric Laurent2035ac82015-03-05 15:18:44 -08001166 return true;
1167 }
1168
1169 /**
1170 * Builder class for {@link FmBandConfig} objects.
1171 */
1172 public static class Builder {
1173 private final BandDescriptor mDescriptor;
1174 private boolean mStereo;
1175 private boolean mRds;
1176 private boolean mTa;
1177 private boolean mAf;
Sanket Agarwal7058e4c2015-10-08 14:14:20 -07001178 private boolean mEa;
Eric Laurent2035ac82015-03-05 15:18:44 -08001179
1180 /**
1181 * Constructs a new Builder with the defaults from an {@link FmBandDescriptor} .
1182 * @param descriptor the FmBandDescriptor defaults are read from .
1183 */
1184 public Builder(FmBandDescriptor descriptor) {
1185 mDescriptor = new BandDescriptor(descriptor.getRegion(), descriptor.getType(),
1186 descriptor.getLowerLimit(), descriptor.getUpperLimit(),
1187 descriptor.getSpacing());
1188 mStereo = descriptor.isStereoSupported();
1189 mRds = descriptor.isRdsSupported();
1190 mTa = descriptor.isTaSupported();
1191 mAf = descriptor.isAfSupported();
Sanket Agarwal7058e4c2015-10-08 14:14:20 -07001192 mEa = descriptor.isEaSupported();
Eric Laurent2035ac82015-03-05 15:18:44 -08001193 }
1194
1195 /**
1196 * Constructs a new Builder from a given {@link FmBandConfig}
1197 * @param config the FmBandConfig object whose data will be reused in the new Builder.
1198 */
1199 public Builder(FmBandConfig config) {
1200 mDescriptor = new BandDescriptor(config.getRegion(), config.getType(),
1201 config.getLowerLimit(), config.getUpperLimit(), config.getSpacing());
1202 mStereo = config.getStereo();
1203 mRds = config.getRds();
1204 mTa = config.getTa();
1205 mAf = config.getAf();
Sanket Agarwal7058e4c2015-10-08 14:14:20 -07001206 mEa = config.getEa();
Eric Laurent2035ac82015-03-05 15:18:44 -08001207 }
1208
1209 /**
1210 * Combines all of the parameters that have been set and return a new
1211 * {@link FmBandConfig} object.
1212 * @return a new {@link FmBandConfig} object
1213 */
1214 public FmBandConfig build() {
1215 FmBandConfig config = new FmBandConfig(mDescriptor.getRegion(),
1216 mDescriptor.getType(), mDescriptor.getLowerLimit(),
1217 mDescriptor.getUpperLimit(), mDescriptor.getSpacing(),
Sanket Agarwal7058e4c2015-10-08 14:14:20 -07001218 mStereo, mRds, mTa, mAf, mEa);
Eric Laurent2035ac82015-03-05 15:18:44 -08001219 return config;
1220 }
1221
1222 /** Set stereo enable state
1223 * @param state The new enable state.
1224 * @return the same Builder instance.
1225 */
1226 public Builder setStereo(boolean state) {
1227 mStereo = state;
1228 return this;
1229 }
1230
1231 /** Set RDS or RBDS(if region is ITU2) enable state
1232 * @param state The new enable state.
1233 * @return the same Builder instance.
1234 */
1235 public Builder setRds(boolean state) {
1236 mRds = state;
1237 return this;
1238 }
1239
1240 /** Set Traffic announcement enable state
1241 * @param state The new enable state.
1242 * @return the same Builder instance.
1243 */
1244 public Builder setTa(boolean state) {
1245 mTa = state;
1246 return this;
1247 }
1248
1249 /** Set Alternate Frequency Switching enable state
1250 * @param state The new enable state.
1251 * @return the same Builder instance.
1252 */
1253 public Builder setAf(boolean state) {
1254 mAf = state;
1255 return this;
1256 }
Sanket Agarwal7058e4c2015-10-08 14:14:20 -07001257
1258 /** Set Emergency Announcement enable state
1259 * @param state The new enable state.
1260 * @return the same Builder instance.
1261 */
1262 public Builder setEa(boolean state) {
1263 mEa = state;
1264 return this;
1265 }
Eric Laurent2035ac82015-03-05 15:18:44 -08001266 };
1267 }
1268
1269 /** AM band configuration.
1270 * @see #BAND_AM */
1271 public static class AmBandConfig extends BandConfig {
1272 private final boolean mStereo;
1273
Tomasz Wasilczykf58305d2017-12-28 14:03:15 -08001274 /** @hide */
1275 public AmBandConfig(AmBandDescriptor descriptor) {
Eric Laurent2035ac82015-03-05 15:18:44 -08001276 super((BandDescriptor)descriptor);
1277 mStereo = descriptor.isStereoSupported();
1278 }
1279
1280 AmBandConfig(int region, int type, int lowerLimit, int upperLimit, int spacing,
1281 boolean stereo) {
1282 super(region, type, lowerLimit, upperLimit, spacing);
1283 mStereo = stereo;
1284 }
1285
1286 /** Get stereo enable state
1287 * @return the enable state.
1288 */
1289 public boolean getStereo() {
1290 return mStereo;
1291 }
1292
1293 private AmBandConfig(Parcel in) {
1294 super(in);
1295 mStereo = in.readByte() == 1;
1296 }
1297
Jeff Sharkey9e8f83d2019-02-28 12:06:45 -07001298 public static final @android.annotation.NonNull Parcelable.Creator<AmBandConfig> CREATOR
Eric Laurent2035ac82015-03-05 15:18:44 -08001299 = new Parcelable.Creator<AmBandConfig>() {
1300 public AmBandConfig createFromParcel(Parcel in) {
1301 return new AmBandConfig(in);
1302 }
1303
1304 public AmBandConfig[] newArray(int size) {
1305 return new AmBandConfig[size];
1306 }
1307 };
1308
1309 @Override
1310 public void writeToParcel(Parcel dest, int flags) {
1311 super.writeToParcel(dest, flags);
1312 dest.writeByte((byte) (mStereo ? 1 : 0));
1313 }
1314
1315 @Override
1316 public int describeContents() {
1317 return 0;
1318 }
1319
1320 @Override
1321 public String toString() {
1322 return "AmBandConfig [" + super.toString()
1323 + ", mStereo=" + mStereo + "]";
1324 }
1325
1326 @Override
1327 public int hashCode() {
1328 final int prime = 31;
1329 int result = super.hashCode();
1330 result = prime * result + (mStereo ? 1 : 0);
1331 return result;
1332 }
1333
1334 @Override
1335 public boolean equals(Object obj) {
1336 if (this == obj)
1337 return true;
1338 if (!super.equals(obj))
1339 return false;
1340 if (!(obj instanceof AmBandConfig))
1341 return false;
1342 AmBandConfig other = (AmBandConfig) obj;
1343 if (mStereo != other.getStereo())
1344 return false;
1345 return true;
1346 }
1347
1348 /**
1349 * Builder class for {@link AmBandConfig} objects.
1350 */
1351 public static class Builder {
1352 private final BandDescriptor mDescriptor;
1353 private boolean mStereo;
1354
1355 /**
1356 * Constructs a new Builder with the defaults from an {@link AmBandDescriptor} .
1357 * @param descriptor the FmBandDescriptor defaults are read from .
1358 */
1359 public Builder(AmBandDescriptor descriptor) {
1360 mDescriptor = new BandDescriptor(descriptor.getRegion(), descriptor.getType(),
1361 descriptor.getLowerLimit(), descriptor.getUpperLimit(),
1362 descriptor.getSpacing());
1363 mStereo = descriptor.isStereoSupported();
1364 }
1365
1366 /**
1367 * Constructs a new Builder from a given {@link AmBandConfig}
1368 * @param config the FmBandConfig object whose data will be reused in the new Builder.
1369 */
1370 public Builder(AmBandConfig config) {
1371 mDescriptor = new BandDescriptor(config.getRegion(), config.getType(),
1372 config.getLowerLimit(), config.getUpperLimit(), config.getSpacing());
1373 mStereo = config.getStereo();
1374 }
1375
1376 /**
1377 * Combines all of the parameters that have been set and return a new
1378 * {@link AmBandConfig} object.
1379 * @return a new {@link AmBandConfig} object
1380 */
1381 public AmBandConfig build() {
1382 AmBandConfig config = new AmBandConfig(mDescriptor.getRegion(),
1383 mDescriptor.getType(), mDescriptor.getLowerLimit(),
1384 mDescriptor.getUpperLimit(), mDescriptor.getSpacing(),
1385 mStereo);
1386 return config;
1387 }
1388
1389 /** Set stereo enable state
1390 * @param state The new enable state.
1391 * @return the same Builder instance.
1392 */
1393 public Builder setStereo(boolean state) {
1394 mStereo = state;
1395 return this;
1396 }
1397 };
1398 }
1399
Tomasz Wasilczykeab3e552018-01-11 20:12:10 -08001400 /** Radio program information. */
Eric Laurent2035ac82015-03-05 15:18:44 -08001401 public static class ProgramInfo implements Parcelable {
1402
Tomasz Wasilczykeab3e552018-01-11 20:12:10 -08001403 // sourced from hardware/interfaces/broadcastradio/2.0/types.hal
Tomasz Wasilczykc4cd8232017-07-14 10:46:15 -07001404 private static final int FLAG_LIVE = 1 << 0;
1405 private static final int FLAG_MUTED = 1 << 1;
1406 private static final int FLAG_TRAFFIC_PROGRAM = 1 << 2;
1407 private static final int FLAG_TRAFFIC_ANNOUNCEMENT = 1 << 3;
Tomasz Wasilczykeab3e552018-01-11 20:12:10 -08001408 private static final int FLAG_TUNED = 1 << 4;
1409 private static final int FLAG_STEREO = 1 << 5;
Tomasz Wasilczyk5fb600b2017-03-24 14:55:08 -07001410
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -07001411 @NonNull private final ProgramSelector mSelector;
Tomasz Wasilczykeab3e552018-01-11 20:12:10 -08001412 @Nullable private final ProgramSelector.Identifier mLogicallyTunedTo;
1413 @Nullable private final ProgramSelector.Identifier mPhysicallyTunedTo;
1414 @NonNull private final Collection<ProgramSelector.Identifier> mRelatedContent;
1415 private final int mInfoFlags;
1416 private final int mSignalQuality;
1417 @Nullable private final RadioMetadata mMetadata;
Tomasz Wasilczyk0f1776d2017-08-03 11:03:49 -07001418 @NonNull private final Map<String, String> mVendorInfo;
Eric Laurent2035ac82015-03-05 15:18:44 -08001419
Tomasz Wasilczyk436128f2018-01-08 16:46:09 -08001420 /** @hide */
Tomasz Wasilczykeab3e552018-01-11 20:12:10 -08001421 public ProgramInfo(@NonNull ProgramSelector selector,
1422 @Nullable ProgramSelector.Identifier logicallyTunedTo,
1423 @Nullable ProgramSelector.Identifier physicallyTunedTo,
1424 @Nullable Collection<ProgramSelector.Identifier> relatedContent,
1425 int infoFlags, int signalQuality, @Nullable RadioMetadata metadata,
1426 @Nullable Map<String, String> vendorInfo) {
1427 mSelector = Objects.requireNonNull(selector);
1428 mLogicallyTunedTo = logicallyTunedTo;
1429 mPhysicallyTunedTo = physicallyTunedTo;
1430 if (relatedContent == null) {
1431 mRelatedContent = Collections.emptyList();
1432 } else {
1433 Preconditions.checkCollectionElementsNotNull(relatedContent, "relatedContent");
1434 mRelatedContent = relatedContent;
1435 }
1436 mInfoFlags = infoFlags;
1437 mSignalQuality = signalQuality;
Eric Laurent2035ac82015-03-05 15:18:44 -08001438 mMetadata = metadata;
Tomasz Wasilczyk0f1776d2017-08-03 11:03:49 -07001439 mVendorInfo = (vendorInfo == null) ? new HashMap<>() : vendorInfo;
Eric Laurent2035ac82015-03-05 15:18:44 -08001440 }
1441
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -07001442 /**
1443 * Program selector, necessary for tuning to a program.
1444 *
1445 * @return the program selector.
1446 */
1447 public @NonNull ProgramSelector getSelector() {
1448 return mSelector;
1449 }
1450
Tomasz Wasilczykeab3e552018-01-11 20:12:10 -08001451 /**
1452 * Identifier currently used for program selection.
1453 *
1454 * This identifier can be used to determine which technology is
1455 * currently being used for reception.
1456 *
1457 * Some program selectors contain tuning information for different radio
1458 * technologies (i.e. FM RDS and DAB). For example, user may tune using
1459 * a ProgramSelector with RDS_PI primary identifier, but the tuner hardware
1460 * may choose to use DAB technology to make actual tuning. This identifier
1461 * must reflect that.
1462 */
1463 public @Nullable ProgramSelector.Identifier getLogicallyTunedTo() {
1464 return mLogicallyTunedTo;
1465 }
1466
1467 /**
1468 * Identifier currently used by hardware to physically tune to a channel.
1469 *
1470 * Some radio technologies broadcast the same program on multiple channels,
1471 * i.e. with RDS AF the same program may be broadcasted on multiple
1472 * alternative frequencies; the same DAB program may be broadcast on
1473 * multiple ensembles. This identifier points to the channel to which the
1474 * radio hardware is physically tuned to.
1475 */
1476 public @Nullable ProgramSelector.Identifier getPhysicallyTunedTo() {
1477 return mPhysicallyTunedTo;
1478 }
1479
1480 /**
1481 * Primary identifiers of related contents.
1482 *
1483 * Some radio technologies provide pointers to other programs that carry
1484 * related content (i.e. DAB soft-links). This field is a list of pointers
1485 * to other programs on the program list.
1486 *
1487 * Please note, that these identifiers does not have to exist on the program
1488 * list - i.e. DAB tuner may provide information on FM RDS alternatives
1489 * despite not supporting FM RDS. If the system has multiple tuners, another
1490 * one may have it on its list.
1491 */
1492 public @Nullable Collection<ProgramSelector.Identifier> getRelatedContent() {
1493 return mRelatedContent;
1494 }
1495
Eric Laurent2035ac82015-03-05 15:18:44 -08001496 /** Main channel expressed in units according to band type.
1497 * Currently all defined band types express channels as frequency in kHz
1498 * @return the program channel
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -07001499 * @deprecated Use {@link getSelector()} instead.
Eric Laurent2035ac82015-03-05 15:18:44 -08001500 */
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -07001501 @Deprecated
Eric Laurent2035ac82015-03-05 15:18:44 -08001502 public int getChannel() {
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -07001503 try {
1504 return (int) mSelector.getFirstId(ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY);
1505 } catch (IllegalArgumentException ex) {
1506 Log.w(TAG, "Not an AM/FM program");
1507 return 0;
1508 }
Eric Laurent2035ac82015-03-05 15:18:44 -08001509 }
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -07001510
Eric Laurent2035ac82015-03-05 15:18:44 -08001511 /** Sub channel ID. E.g 1 for HD radio HD1
1512 * @return the program sub channel
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -07001513 * @deprecated Use {@link getSelector()} instead.
Eric Laurent2035ac82015-03-05 15:18:44 -08001514 */
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -07001515 @Deprecated
Eric Laurent2035ac82015-03-05 15:18:44 -08001516 public int getSubChannel() {
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -07001517 try {
1518 return (int) mSelector.getFirstId(
1519 ProgramSelector.IDENTIFIER_TYPE_HD_SUBCHANNEL) + 1;
1520 } catch (IllegalArgumentException ex) {
1521 // this is a normal behavior for analog AM/FM selector
1522 return 0;
1523 }
Eric Laurent2035ac82015-03-05 15:18:44 -08001524 }
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -07001525
Eric Laurent2035ac82015-03-05 15:18:44 -08001526 /** {@code true} if the tuner is currently tuned on a valid station
1527 * @return {@code true} if currently tuned, {@code false} otherwise.
1528 */
1529 public boolean isTuned() {
Tomasz Wasilczykeab3e552018-01-11 20:12:10 -08001530 return (mInfoFlags & FLAG_TUNED) != 0;
Eric Laurent2035ac82015-03-05 15:18:44 -08001531 }
Tomasz Wasilczykeab3e552018-01-11 20:12:10 -08001532
Eric Laurent2035ac82015-03-05 15:18:44 -08001533 /** {@code true} if the received program is stereo
1534 * @return {@code true} if stereo, {@code false} otherwise.
1535 */
1536 public boolean isStereo() {
Tomasz Wasilczykeab3e552018-01-11 20:12:10 -08001537 return (mInfoFlags & FLAG_STEREO) != 0;
Eric Laurent2035ac82015-03-05 15:18:44 -08001538 }
Tomasz Wasilczykeab3e552018-01-11 20:12:10 -08001539
Eric Laurent2035ac82015-03-05 15:18:44 -08001540 /** {@code true} if the received program is digital (e.g HD radio)
1541 * @return {@code true} if digital, {@code false} otherwise.
Tomasz Wasilczykeab3e552018-01-11 20:12:10 -08001542 * @deprecated Use {@link getLogicallyTunedTo()} instead.
Eric Laurent2035ac82015-03-05 15:18:44 -08001543 */
Tomasz Wasilczykeab3e552018-01-11 20:12:10 -08001544 @Deprecated
Eric Laurent2035ac82015-03-05 15:18:44 -08001545 public boolean isDigital() {
Tomasz Wasilczykeab3e552018-01-11 20:12:10 -08001546 ProgramSelector.Identifier id = mLogicallyTunedTo;
1547 if (id == null) id = mSelector.getPrimaryId();
1548
1549 int type = id.getType();
1550 return (type != ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY
1551 && type != ProgramSelector.IDENTIFIER_TYPE_RDS_PI);
Eric Laurent2035ac82015-03-05 15:18:44 -08001552 }
Tomasz Wasilczyk5fb600b2017-03-24 14:55:08 -07001553
1554 /**
1555 * {@code true} if the program is currently playing live stream.
1556 * This may result in a slightly altered reception parameters,
1557 * usually targetted at reduced latency.
Tomasz Wasilczyk5fb600b2017-03-24 14:55:08 -07001558 */
1559 public boolean isLive() {
Tomasz Wasilczykeab3e552018-01-11 20:12:10 -08001560 return (mInfoFlags & FLAG_LIVE) != 0;
Tomasz Wasilczyk5fb600b2017-03-24 14:55:08 -07001561 }
1562
1563 /**
1564 * {@code true} if radio stream is not playing, ie. due to bad reception
1565 * conditions or buffering. In this state volume knob MAY be disabled to
1566 * prevent user increasing volume too much.
1567 * It does NOT mean the user has muted audio.
Tomasz Wasilczyk5fb600b2017-03-24 14:55:08 -07001568 */
1569 public boolean isMuted() {
Tomasz Wasilczykeab3e552018-01-11 20:12:10 -08001570 return (mInfoFlags & FLAG_MUTED) != 0;
Tomasz Wasilczyk5fb600b2017-03-24 14:55:08 -07001571 }
1572
Tomasz Wasilczykc4cd8232017-07-14 10:46:15 -07001573 /**
1574 * {@code true} if radio station transmits traffic information
1575 * regularily.
1576 */
1577 public boolean isTrafficProgram() {
Tomasz Wasilczykeab3e552018-01-11 20:12:10 -08001578 return (mInfoFlags & FLAG_TRAFFIC_PROGRAM) != 0;
Tomasz Wasilczykc4cd8232017-07-14 10:46:15 -07001579 }
1580
1581 /**
1582 * {@code true} if radio station transmits traffic information
1583 * at the very moment.
1584 */
1585 public boolean isTrafficAnnouncementActive() {
Tomasz Wasilczykeab3e552018-01-11 20:12:10 -08001586 return (mInfoFlags & FLAG_TRAFFIC_ANNOUNCEMENT) != 0;
Tomasz Wasilczykc4cd8232017-07-14 10:46:15 -07001587 }
1588
Tomasz Wasilczykeab3e552018-01-11 20:12:10 -08001589 /**
1590 * Signal quality (as opposed to the name) indication from 0 (no signal)
1591 * to 100 (excellent)
1592 * @return the signal quality indication.
Eric Laurent2035ac82015-03-05 15:18:44 -08001593 */
1594 public int getSignalStrength() {
Tomasz Wasilczykeab3e552018-01-11 20:12:10 -08001595 return mSignalQuality;
Eric Laurent2035ac82015-03-05 15:18:44 -08001596 }
Tomasz Wasilczykeab3e552018-01-11 20:12:10 -08001597
Eric Laurent2035ac82015-03-05 15:18:44 -08001598 /** Metadata currently received from this station.
1599 * null if no metadata have been received
1600 * @return current meta data received from this program.
1601 */
1602 public RadioMetadata getMetadata() {
1603 return mMetadata;
1604 }
1605
Tomasz Wasilczyk6849aa82017-03-29 11:59:23 -07001606 /**
Tomasz Wasilczyk0f1776d2017-08-03 11:03:49 -07001607 * A map of vendor-specific opaque strings, passed from HAL without changes.
1608 * Format of these strings can vary across vendors.
Tomasz Wasilczyk6849aa82017-03-29 11:59:23 -07001609 *
1610 * It may be used for extra features, that's not supported by a platform,
Tomasz Wasilczyk0f1776d2017-08-03 11:03:49 -07001611 * for example: paid-service=true; bitrate=320kbps.
Tomasz Wasilczyk6849aa82017-03-29 11:59:23 -07001612 *
Tomasz Wasilczyk0f1776d2017-08-03 11:03:49 -07001613 * Keys must be prefixed with unique vendor Java-style namespace,
1614 * eg. 'com.somecompany.parameter1'.
Tomasz Wasilczyk6849aa82017-03-29 11:59:23 -07001615 */
Tomasz Wasilczyk0f1776d2017-08-03 11:03:49 -07001616 public @NonNull Map<String, String> getVendorInfo() {
1617 return mVendorInfo;
Tomasz Wasilczyk6849aa82017-03-29 11:59:23 -07001618 }
1619
Eric Laurent2035ac82015-03-05 15:18:44 -08001620 private ProgramInfo(Parcel in) {
Tomasz Wasilczykeab3e552018-01-11 20:12:10 -08001621 mSelector = Objects.requireNonNull(in.readTypedObject(ProgramSelector.CREATOR));
1622 mLogicallyTunedTo = in.readTypedObject(ProgramSelector.Identifier.CREATOR);
1623 mPhysicallyTunedTo = in.readTypedObject(ProgramSelector.Identifier.CREATOR);
1624 mRelatedContent = in.createTypedArrayList(ProgramSelector.Identifier.CREATOR);
1625 mInfoFlags = in.readInt();
1626 mSignalQuality = in.readInt();
1627 mMetadata = in.readTypedObject(RadioMetadata.CREATOR);
Tomasz Wasilczyk436128f2018-01-08 16:46:09 -08001628 mVendorInfo = Utils.readStringMap(in);
Eric Laurent2035ac82015-03-05 15:18:44 -08001629 }
1630
Jeff Sharkey9e8f83d2019-02-28 12:06:45 -07001631 public static final @android.annotation.NonNull Parcelable.Creator<ProgramInfo> CREATOR
Eric Laurent2035ac82015-03-05 15:18:44 -08001632 = new Parcelable.Creator<ProgramInfo>() {
1633 public ProgramInfo createFromParcel(Parcel in) {
1634 return new ProgramInfo(in);
1635 }
1636
1637 public ProgramInfo[] newArray(int size) {
1638 return new ProgramInfo[size];
1639 }
1640 };
1641
1642 @Override
1643 public void writeToParcel(Parcel dest, int flags) {
Tomasz Wasilczykeab3e552018-01-11 20:12:10 -08001644 dest.writeTypedObject(mSelector, flags);
1645 dest.writeTypedObject(mLogicallyTunedTo, flags);
1646 dest.writeTypedObject(mPhysicallyTunedTo, flags);
1647 Utils.writeTypedCollection(dest, mRelatedContent);
1648 dest.writeInt(mInfoFlags);
1649 dest.writeInt(mSignalQuality);
1650 dest.writeTypedObject(mMetadata, flags);
Tomasz Wasilczyk436128f2018-01-08 16:46:09 -08001651 Utils.writeStringMap(dest, mVendorInfo);
Eric Laurent2035ac82015-03-05 15:18:44 -08001652 }
1653
1654 @Override
1655 public int describeContents() {
1656 return 0;
1657 }
1658
1659 @Override
1660 public String toString() {
Tomasz Wasilczykeab3e552018-01-11 20:12:10 -08001661 return "ProgramInfo"
1662 + " [selector=" + mSelector
1663 + ", logicallyTunedTo=" + Objects.toString(mLogicallyTunedTo)
1664 + ", physicallyTunedTo=" + Objects.toString(mPhysicallyTunedTo)
1665 + ", relatedContent=" + mRelatedContent.size()
1666 + ", infoFlags=" + mInfoFlags
1667 + ", mSignalQuality=" + mSignalQuality
1668 + ", mMetadata=" + Objects.toString(mMetadata)
Eric Laurent2035ac82015-03-05 15:18:44 -08001669 + "]";
1670 }
1671
1672 @Override
1673 public int hashCode() {
Tomasz Wasilczykeab3e552018-01-11 20:12:10 -08001674 return Objects.hash(mSelector, mLogicallyTunedTo, mPhysicallyTunedTo,
1675 mRelatedContent, mInfoFlags, mSignalQuality, mMetadata, mVendorInfo);
Eric Laurent2035ac82015-03-05 15:18:44 -08001676 }
1677
1678 @Override
1679 public boolean equals(Object obj) {
Tomasz Wasilczykeab3e552018-01-11 20:12:10 -08001680 if (this == obj) return true;
1681 if (!(obj instanceof ProgramInfo)) return false;
Eric Laurent2035ac82015-03-05 15:18:44 -08001682 ProgramInfo other = (ProgramInfo) obj;
Tomasz Wasilczykeab3e552018-01-11 20:12:10 -08001683
1684 if (!Objects.equals(mSelector, other.mSelector)) return false;
1685 if (!Objects.equals(mLogicallyTunedTo, other.mLogicallyTunedTo)) return false;
1686 if (!Objects.equals(mPhysicallyTunedTo, other.mPhysicallyTunedTo)) return false;
1687 if (!Objects.equals(mRelatedContent, other.mRelatedContent)) return false;
1688 if (mInfoFlags != other.mInfoFlags) return false;
1689 if (mSignalQuality != other.mSignalQuality) return false;
1690 if (!Objects.equals(mMetadata, other.mMetadata)) return false;
1691 if (!Objects.equals(mVendorInfo, other.mVendorInfo)) return false;
1692
Eric Laurent2035ac82015-03-05 15:18:44 -08001693 return true;
1694 }
1695 }
1696
1697
1698 /**
1699 * Returns a list of descriptors for all broadcast radio modules present on the device.
1700 * @param modules An List of {@link ModuleProperties} where the list will be returned.
1701 * @return
1702 * <ul>
1703 * <li>{@link #STATUS_OK} in case of success, </li>
1704 * <li>{@link #STATUS_ERROR} in case of unspecified error, </li>
1705 * <li>{@link #STATUS_NO_INIT} if the native service cannot be reached, </li>
1706 * <li>{@link #STATUS_BAD_VALUE} if modules is null, </li>
1707 * <li>{@link #STATUS_DEAD_OBJECT} if the binder transaction to the native service fails, </li>
1708 * </ul>
1709 */
Tomasz Wasilczyk749e3dc2017-07-28 13:20:41 -07001710 @RequiresPermission(Manifest.permission.ACCESS_BROADCAST_RADIO)
Tomasz Wasilczykd15c9df2017-06-13 10:10:36 -07001711 public int listModules(List<ModuleProperties> modules) {
1712 if (modules == null) {
1713 Log.e(TAG, "the output list must not be empty");
1714 return STATUS_BAD_VALUE;
1715 }
1716
Tomasz Wasilczyk14752372017-06-21 11:58:21 -07001717 Log.d(TAG, "Listing available tuners...");
Tomasz Wasilczykd15c9df2017-06-13 10:10:36 -07001718 List<ModuleProperties> returnedList;
1719 try {
1720 returnedList = mService.listModules();
1721 } catch (RemoteException e) {
1722 Log.e(TAG, "Failed listing available tuners", e);
1723 return STATUS_DEAD_OBJECT;
1724 }
1725
1726 if (returnedList == null) {
1727 Log.e(TAG, "Returned list was a null");
1728 return STATUS_ERROR;
1729 }
1730
1731 modules.addAll(returnedList);
1732 return STATUS_OK;
1733 }
1734
1735 private native int nativeListModules(List<ModuleProperties> modules);
Eric Laurent2035ac82015-03-05 15:18:44 -08001736
1737 /**
1738 * Open an interface to control a tuner on a given broadcast radio module.
1739 * Optionally selects and applies the configuration passed as "config" argument.
1740 * @param moduleId radio module identifier {@link ModuleProperties#getId()}. Mandatory.
1741 * @param config desired band and configuration to apply when enabling the hardware module.
1742 * optional, can be null.
1743 * @param withAudio {@code true} to request a tuner with an audio source.
1744 * This tuner is intended for live listening or recording or a radio program.
1745 * If {@code false}, the tuner can only be used to retrieve program informations.
1746 * @param callback {@link RadioTuner.Callback} interface. Mandatory.
1747 * @param handler the Handler on which the callbacks will be received.
1748 * Can be null if default handler is OK.
1749 * @return a valid {@link RadioTuner} interface in case of success or null in case of error.
1750 */
Tomasz Wasilczyk749e3dc2017-07-28 13:20:41 -07001751 @RequiresPermission(Manifest.permission.ACCESS_BROADCAST_RADIO)
Eric Laurent2035ac82015-03-05 15:18:44 -08001752 public RadioTuner openTuner(int moduleId, BandConfig config, boolean withAudio,
1753 RadioTuner.Callback callback, Handler handler) {
1754 if (callback == null) {
Tomasz Wasilczyk347192e2017-04-04 11:13:44 -07001755 throw new IllegalArgumentException("callback must not be empty");
Eric Laurent2035ac82015-03-05 15:18:44 -08001756 }
Tomasz Wasilczyk347192e2017-04-04 11:13:44 -07001757
Tomasz Wasilczyk9b595f32017-06-21 11:14:19 -07001758 Log.d(TAG, "Opening tuner " + moduleId + "...");
Tomasz Wasilczyk21348172017-04-20 14:02:42 -07001759
Tomasz Wasilczyk9b595f32017-06-21 11:14:19 -07001760 ITuner tuner;
Tomasz Wasilczyk24250ef2017-07-13 15:59:08 -07001761 TunerCallbackAdapter halCallback = new TunerCallbackAdapter(callback, handler);
Tomasz Wasilczyk9b595f32017-06-21 11:14:19 -07001762 try {
1763 tuner = mService.openTuner(moduleId, config, withAudio, halCallback);
Tomasz Wasilczykca98cde2018-01-04 12:26:40 -08001764 } catch (RemoteException | IllegalArgumentException ex) {
1765 Log.e(TAG, "Failed to open tuner", ex);
Tomasz Wasilczyk9b595f32017-06-21 11:14:19 -07001766 return null;
Eric Laurent2035ac82015-03-05 15:18:44 -08001767 }
Tomasz Wasilczyk9b595f32017-06-21 11:14:19 -07001768 if (tuner == null) {
Tomasz Wasilczyk347192e2017-04-04 11:13:44 -07001769 Log.e(TAG, "Failed to open tuner");
Tomasz Wasilczyk9b595f32017-06-21 11:14:19 -07001770 return null;
Tomasz Wasilczyk347192e2017-04-04 11:13:44 -07001771 }
Tomasz Wasilczyk436128f2018-01-08 16:46:09 -08001772 return new TunerAdapter(tuner, halCallback,
1773 config != null ? config.getType() : BAND_INVALID);
Eric Laurent2035ac82015-03-05 15:18:44 -08001774 }
1775
Tomasz Wasilczykf151a7b2018-01-11 16:03:46 -08001776 private final Map<Announcement.OnListUpdatedListener, ICloseHandle> mAnnouncementListeners =
1777 new HashMap<>();
1778
1779 /**
1780 * Adds new announcement listener.
1781 *
1782 * @param enabledAnnouncementTypes a set of announcement types to listen to
1783 * @param listener announcement listener
1784 */
1785 @RequiresPermission(Manifest.permission.ACCESS_BROADCAST_RADIO)
1786 public void addAnnouncementListener(@NonNull Set<Integer> enabledAnnouncementTypes,
1787 @NonNull Announcement.OnListUpdatedListener listener) {
1788 addAnnouncementListener(cmd -> cmd.run(), enabledAnnouncementTypes, listener);
1789 }
1790
1791 /**
1792 * Adds new announcement listener with executor.
1793 *
1794 * @param executor the executor
1795 * @param enabledAnnouncementTypes a set of announcement types to listen to
1796 * @param listener announcement listener
1797 */
1798 @RequiresPermission(Manifest.permission.ACCESS_BROADCAST_RADIO)
1799 public void addAnnouncementListener(@NonNull @CallbackExecutor Executor executor,
1800 @NonNull Set<Integer> enabledAnnouncementTypes,
1801 @NonNull Announcement.OnListUpdatedListener listener) {
1802 Objects.requireNonNull(executor);
1803 Objects.requireNonNull(listener);
1804 int[] types = enabledAnnouncementTypes.stream().mapToInt(Integer::intValue).toArray();
1805 IAnnouncementListener listenerIface = new IAnnouncementListener.Stub() {
1806 public void onListUpdated(List<Announcement> activeAnnouncements) {
1807 executor.execute(() -> listener.onListUpdated(activeAnnouncements));
1808 }
1809 };
1810 synchronized (mAnnouncementListeners) {
1811 ICloseHandle closeHandle = null;
1812 try {
1813 closeHandle = mService.addAnnouncementListener(types, listenerIface);
1814 } catch (RemoteException ex) {
1815 ex.rethrowFromSystemServer();
1816 }
1817 Objects.requireNonNull(closeHandle);
1818 ICloseHandle oldCloseHandle = mAnnouncementListeners.put(listener, closeHandle);
1819 if (oldCloseHandle != null) Utils.close(oldCloseHandle);
1820 }
1821 }
1822
1823 /**
1824 * Removes previously registered announcement listener.
1825 *
1826 * @param listener announcement listener, previously registered with
1827 * {@link addAnnouncementListener}
1828 */
1829 @RequiresPermission(Manifest.permission.ACCESS_BROADCAST_RADIO)
1830 public void removeAnnouncementListener(@NonNull Announcement.OnListUpdatedListener listener) {
1831 Objects.requireNonNull(listener);
1832 synchronized (mAnnouncementListeners) {
1833 ICloseHandle closeHandle = mAnnouncementListeners.remove(listener);
1834 if (closeHandle != null) Utils.close(closeHandle);
1835 }
1836 }
1837
Tomasz Wasilczyk347192e2017-04-04 11:13:44 -07001838 @NonNull private final Context mContext;
Tomasz Wasilczyk9b595f32017-06-21 11:14:19 -07001839 @NonNull private final IRadioService mService;
Eric Laurent2035ac82015-03-05 15:18:44 -08001840
1841 /**
1842 * @hide
1843 */
Tomasz Wasilczyk347192e2017-04-04 11:13:44 -07001844 public RadioManager(@NonNull Context context) throws ServiceNotFoundException {
Eric Laurent2035ac82015-03-05 15:18:44 -08001845 mContext = context;
Tomasz Wasilczyk9b595f32017-06-21 11:14:19 -07001846 mService = IRadioService.Stub.asInterface(
1847 ServiceManager.getServiceOrThrow(Context.RADIO_SERVICE));
Eric Laurent2035ac82015-03-05 15:18:44 -08001848 }
1849}