blob: b00f6033976e1b75996b53c9359255f2cafe8511 [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;
Tomasz Wasilczyk749e3dc2017-07-28 13:20:41 -070024import android.annotation.RequiresPermission;
Eric Laurent2035ac82015-03-05 15:18:44 -080025import android.annotation.SystemApi;
Jeff Sharkeyd86b8fe2017-06-02 17:36:26 -060026import android.annotation.SystemService;
Eric Laurent2035ac82015-03-05 15:18:44 -080027import android.content.Context;
28import android.os.Handler;
29import android.os.Parcel;
30import android.os.Parcelable;
Tomasz Wasilczyk347192e2017-04-04 11:13:44 -070031import android.os.RemoteException;
32import android.os.ServiceManager;
33import android.os.ServiceManager.ServiceNotFoundException;
Tomasz Wasilczyk6849aa82017-03-29 11:59:23 -070034import android.text.TextUtils;
Tomasz Wasilczyk347192e2017-04-04 11:13:44 -070035import android.util.Log;
36
Tomasz Wasilczykeab3e552018-01-11 20:12:10 -080037import com.android.internal.util.Preconditions;
38
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -070039import java.lang.annotation.Retention;
40import java.lang.annotation.RetentionPolicy;
Eric Laurent2035ac82015-03-05 15:18:44 -080041import java.util.Arrays;
Tomasz Wasilczykeab3e552018-01-11 20:12:10 -080042import java.util.Collection;
43import java.util.Collections;
Tomasz Wasilczyk0f1776d2017-08-03 11:03:49 -070044import java.util.HashMap;
Tomasz Wasilczykd15c9df2017-06-13 10:10:36 -070045import java.util.List;
Tomasz Wasilczyk0f1776d2017-08-03 11:03:49 -070046import java.util.Map;
Tomasz Wasilczykf58305d2017-12-28 14:03:15 -080047import java.util.Objects;
Tomasz Wasilczyk54587ce2017-07-16 15:15:40 -070048import java.util.Set;
Tomasz Wasilczykf151a7b2018-01-11 16:03:46 -080049import java.util.concurrent.Executor;
Tomasz Wasilczyk54587ce2017-07-16 15:15:40 -070050import java.util.stream.Collectors;
Eric Laurent2035ac82015-03-05 15:18:44 -080051
52/**
53 * The RadioManager class allows to control a broadcast radio tuner present on the device.
54 * It provides data structures and methods to query for available radio modules, list their
55 * properties and open an interface to control tuning operations and receive callbacks when
56 * asynchronous operations complete or events occur.
57 * @hide
58 */
59@SystemApi
Jeff Sharkeyd86b8fe2017-06-02 17:36:26 -060060@SystemService(Context.RADIO_SERVICE)
Eric Laurent2035ac82015-03-05 15:18:44 -080061public class RadioManager {
Tomasz Wasilczyk6b4b6462017-07-19 10:52:28 -070062 private static final String TAG = "BroadcastRadio.manager";
Eric Laurent2035ac82015-03-05 15:18:44 -080063
64 /** Method return status: successful operation */
65 public static final int STATUS_OK = 0;
66 /** Method return status: unspecified error */
67 public static final int STATUS_ERROR = Integer.MIN_VALUE;
68 /** Method return status: permission denied */
69 public static final int STATUS_PERMISSION_DENIED = -1;
70 /** Method return status: initialization failure */
71 public static final int STATUS_NO_INIT = -19;
72 /** Method return status: invalid argument provided */
73 public static final int STATUS_BAD_VALUE = -22;
74 /** Method return status: cannot reach service */
75 public static final int STATUS_DEAD_OBJECT = -32;
76 /** Method return status: invalid or out of sequence operation */
77 public static final int STATUS_INVALID_OPERATION = -38;
78 /** Method return status: time out before operation completion */
79 public static final int STATUS_TIMED_OUT = -110;
80
81
82 // keep in sync with radio_class_t in /system/core/incluse/system/radio.h
83 /** Radio module class supporting FM (including HD radio) and AM */
84 public static final int CLASS_AM_FM = 0;
85 /** Radio module class supporting satellite radio */
86 public static final int CLASS_SAT = 1;
87 /** Radio module class supporting Digital terrestrial radio */
88 public static final int CLASS_DT = 2;
89
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -070090 public static final int BAND_INVALID = -1;
Eric Laurent2035ac82015-03-05 15:18:44 -080091 /** AM radio band (LW/MW/SW).
92 * @see BandDescriptor */
93 public static final int BAND_AM = 0;
94 /** FM radio band.
95 * @see BandDescriptor */
96 public static final int BAND_FM = 1;
97 /** FM HD radio or DRM band.
98 * @see BandDescriptor */
99 public static final int BAND_FM_HD = 2;
100 /** AM HD radio or DRM band.
101 * @see BandDescriptor */
102 public static final int BAND_AM_HD = 3;
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -0700103 @IntDef(prefix = { "BAND_" }, value = {
104 BAND_INVALID,
105 BAND_AM,
106 BAND_FM,
107 BAND_AM_HD,
108 BAND_FM_HD,
109 })
110 @Retention(RetentionPolicy.SOURCE)
111 public @interface Band {}
Eric Laurent2035ac82015-03-05 15:18:44 -0800112
113 // keep in sync with radio_region_t in /system/core/incluse/system/radio.h
114 /** Africa, Europe.
115 * @see BandDescriptor */
116 public static final int REGION_ITU_1 = 0;
117 /** Americas.
118 * @see BandDescriptor */
119 public static final int REGION_ITU_2 = 1;
120 /** Russia.
121 * @see BandDescriptor */
122 public static final int REGION_OIRT = 2;
123 /** Japan.
124 * @see BandDescriptor */
125 public static final int REGION_JAPAN = 3;
126 /** Korea.
127 * @see BandDescriptor */
128 public static final int REGION_KOREA = 4;
129
Tomasz Wasilczykce40fe92018-01-04 20:52:39 -0800130 /**
131 * Forces mono audio stream reception.
132 *
133 * Analog broadcasts can recover poor reception conditions by jointing
134 * stereo channels into one. Mainly for, but not limited to AM/FM.
135 */
136 public static final int CONFIG_FORCE_MONO = 1;
137 /**
138 * Forces the analog playback for the supporting radio technology.
139 *
140 * User may disable digital playback for FM HD Radio or hybrid FM/DAB with
141 * this option. This is purely user choice, ie. does not reflect digital-
142 * analog handover state managed from the HAL implementation side.
143 *
144 * Some radio technologies may not support this, ie. DAB.
145 */
146 public static final int CONFIG_FORCE_ANALOG = 2;
147 /**
148 * Forces the digital playback for the supporting radio technology.
149 *
150 * User may disable digital-analog handover that happens with poor
151 * reception conditions. With digital forced, the radio will remain silent
152 * instead of switching to analog channel if it's available. This is purely
153 * user choice, it does not reflect the actual state of handover.
154 */
155 public static final int CONFIG_FORCE_DIGITAL = 3;
156 /**
157 * RDS Alternative Frequencies.
158 *
159 * If set and the currently tuned RDS station broadcasts on multiple
160 * channels, radio tuner automatically switches to the best available
161 * alternative.
162 */
163 public static final int CONFIG_RDS_AF = 4;
164 /**
165 * RDS region-specific program lock-down.
166 *
167 * Allows user to lock to the current region as they move into the
168 * other region.
169 */
170 public static final int CONFIG_RDS_REG = 5;
171 /** Enables DAB-DAB hard- and implicit-linking (the same content). */
172 public static final int CONFIG_DAB_DAB_LINKING = 6;
173 /** Enables DAB-FM hard- and implicit-linking (the same content). */
174 public static final int CONFIG_DAB_FM_LINKING = 7;
175 /** Enables DAB-DAB soft-linking (related content). */
176 public static final int CONFIG_DAB_DAB_SOFT_LINKING = 8;
177 /** Enables DAB-FM soft-linking (related content). */
178 public static final int CONFIG_DAB_FM_SOFT_LINKING = 9;
179
180 /** @hide */
181 @IntDef(prefix = { "CONFIG_" }, value = {
182 CONFIG_FORCE_MONO,
183 CONFIG_FORCE_ANALOG,
184 CONFIG_FORCE_DIGITAL,
185 CONFIG_RDS_AF,
186 CONFIG_RDS_REG,
187 CONFIG_DAB_DAB_LINKING,
188 CONFIG_DAB_FM_LINKING,
189 CONFIG_DAB_DAB_SOFT_LINKING,
190 CONFIG_DAB_FM_SOFT_LINKING,
191 })
192 @Retention(RetentionPolicy.SOURCE)
193 public @interface ConfigFlag {}
194
Eric Laurent2035ac82015-03-05 15:18:44 -0800195 /*****************************************************************************
196 * Lists properties, options and radio bands supported by a given broadcast radio module.
197 * Each module has a unique ID used to address it when calling RadioManager APIs.
198 * Module properties are returned by {@link #listModules(List <ModuleProperties>)} method.
199 ****************************************************************************/
200 public static class ModuleProperties implements Parcelable {
201
202 private final int mId;
Tomasz Wasilczykd15c9df2017-06-13 10:10:36 -0700203 @NonNull private final String mServiceName;
Eric Laurent2035ac82015-03-05 15:18:44 -0800204 private final int mClassId;
205 private final String mImplementor;
206 private final String mProduct;
207 private final String mVersion;
208 private final String mSerial;
209 private final int mNumTuners;
210 private final int mNumAudioSources;
211 private final boolean mIsCaptureSupported;
212 private final BandDescriptor[] mBands;
Tomasz Wasilczyk6849aa82017-03-29 11:59:23 -0700213 private final boolean mIsBgScanSupported;
Tomasz Wasilczyk54587ce2017-07-16 15:15:40 -0700214 private final Set<Integer> mSupportedProgramTypes;
215 private final Set<Integer> mSupportedIdentifierTypes;
Tomasz Wasilczyk0f1776d2017-08-03 11:03:49 -0700216 @NonNull private final Map<String, String> mVendorInfo;
Eric Laurent2035ac82015-03-05 15:18:44 -0800217
Tomasz Wasilczykd65b3ca2017-12-13 08:26:25 -0800218 /** @hide */
219 public ModuleProperties(int id, String serviceName, int classId, String implementor,
Tomasz Wasilczykd15c9df2017-06-13 10:10:36 -0700220 String product, String version, String serial, int numTuners, int numAudioSources,
221 boolean isCaptureSupported, BandDescriptor[] bands, boolean isBgScanSupported,
Tomasz Wasilczyk54587ce2017-07-16 15:15:40 -0700222 @ProgramSelector.ProgramType int[] supportedProgramTypes,
223 @ProgramSelector.IdentifierType int[] supportedIdentifierTypes,
Tomasz Wasilczyk0f1776d2017-08-03 11:03:49 -0700224 Map<String, String> vendorInfo) {
Eric Laurent2035ac82015-03-05 15:18:44 -0800225 mId = id;
Tomasz Wasilczykd15c9df2017-06-13 10:10:36 -0700226 mServiceName = TextUtils.isEmpty(serviceName) ? "default" : serviceName;
Eric Laurent2035ac82015-03-05 15:18:44 -0800227 mClassId = classId;
228 mImplementor = implementor;
229 mProduct = product;
230 mVersion = version;
231 mSerial = serial;
232 mNumTuners = numTuners;
233 mNumAudioSources = numAudioSources;
234 mIsCaptureSupported = isCaptureSupported;
235 mBands = bands;
Tomasz Wasilczyk6849aa82017-03-29 11:59:23 -0700236 mIsBgScanSupported = isBgScanSupported;
Tomasz Wasilczyk54587ce2017-07-16 15:15:40 -0700237 mSupportedProgramTypes = arrayToSet(supportedProgramTypes);
238 mSupportedIdentifierTypes = arrayToSet(supportedIdentifierTypes);
Tomasz Wasilczyk0f1776d2017-08-03 11:03:49 -0700239 mVendorInfo = (vendorInfo == null) ? new HashMap<>() : vendorInfo;
Eric Laurent2035ac82015-03-05 15:18:44 -0800240 }
241
Tomasz Wasilczyk54587ce2017-07-16 15:15:40 -0700242 private static Set<Integer> arrayToSet(int[] arr) {
243 return Arrays.stream(arr).boxed().collect(Collectors.toSet());
244 }
245
246 private static int[] setToArray(Set<Integer> set) {
247 return set.stream().mapToInt(Integer::intValue).toArray();
248 }
Eric Laurent2035ac82015-03-05 15:18:44 -0800249
250 /** Unique module identifier provided by the native service.
251 * For use with {@link #openTuner(int, BandConfig, boolean, Callback, Handler)}.
252 * @return the radio module unique identifier.
253 */
254 public int getId() {
255 return mId;
256 }
257
Tomasz Wasilczykd15c9df2017-06-13 10:10:36 -0700258 /**
259 * Module service (driver) name as registered with HIDL.
260 * @return the module service name.
Tomasz Wasilczykd15c9df2017-06-13 10:10:36 -0700261 */
262 public @NonNull String getServiceName() {
263 return mServiceName;
264 }
265
Eric Laurent2035ac82015-03-05 15:18:44 -0800266 /** Module class identifier: {@link #CLASS_AM_FM}, {@link #CLASS_SAT}, {@link #CLASS_DT}
267 * @return the radio module class identifier.
268 */
269 public int getClassId() {
270 return mClassId;
271 }
272
273 /** Human readable broadcast radio module implementor
274 * @return the name of the radio module implementator.
275 */
276 public String getImplementor() {
277 return mImplementor;
278 }
279
280 /** Human readable broadcast radio module product name
281 * @return the radio module product name.
282 */
283 public String getProduct() {
284 return mProduct;
285 }
286
287 /** Human readable broadcast radio module version number
288 * @return the radio module version.
289 */
290 public String getVersion() {
291 return mVersion;
292 }
293
294 /** Radio module serial number.
295 * Can be used for subscription services.
296 * @return the radio module serial number.
297 */
298 public String getSerial() {
299 return mSerial;
300 }
301
302 /** Number of tuners available.
303 * This is the number of tuners that can be open simultaneously.
304 * @return the number of tuners supported.
305 */
306 public int getNumTuners() {
307 return mNumTuners;
308 }
309
310 /** Number tuner audio sources available. Must be less or equal to getNumTuners().
311 * When more than one tuner is supported, one is usually for playback and has one
312 * associated audio source and the other is for pre scanning and building a
313 * program list.
314 * @return the number of audio sources available.
315 */
316 public int getNumAudioSources() {
317 return mNumAudioSources;
318 }
319
320 /** {@code true} if audio capture is possible from radio tuner output.
321 * This indicates if routing to audio devices not connected to the same HAL as the FM radio
322 * is possible (e.g. to USB) or DAR (Digital Audio Recorder) feature can be implemented.
323 * @return {@code true} if audio capture is possible, {@code false} otherwise.
324 */
325 public boolean isCaptureSupported() {
326 return mIsCaptureSupported;
327 }
328
Tomasz Wasilczyke597ce12017-03-24 13:50:53 -0700329 /**
330 * {@code true} if the module supports background scanning. At the given time it may not
331 * be available though, see {@link RadioTuner#startBackgroundScan()}.
332 *
333 * @return {@code true} if background scanning is supported (not necessary available
334 * at a given time), {@code false} otherwise.
Tomasz Wasilczyke597ce12017-03-24 13:50:53 -0700335 */
336 public boolean isBackgroundScanningSupported() {
Tomasz Wasilczyk6849aa82017-03-29 11:59:23 -0700337 return mIsBgScanSupported;
338 }
339
340 /**
Tomasz Wasilczyk54587ce2017-07-16 15:15:40 -0700341 * Checks, if a given program type is supported by this tuner.
342 *
343 * If a program type is supported by radio module, it means it can tune
344 * to ProgramSelector of a given type.
345 *
346 * @return {@code true} if a given program type is supported.
347 */
348 public boolean isProgramTypeSupported(@ProgramSelector.ProgramType int type) {
349 return mSupportedProgramTypes.contains(type);
350 }
351
352 /**
353 * Checks, if a given program identifier is supported by this tuner.
354 *
355 * If an identifier is supported by radio module, it means it can use it for
356 * tuning to ProgramSelector with either primary or secondary Identifier of
357 * a given type.
358 *
359 * @return {@code true} if a given program type is supported.
360 */
361 public boolean isProgramIdentifierSupported(@ProgramSelector.IdentifierType int type) {
362 return mSupportedIdentifierTypes.contains(type);
363 }
364
365 /**
Tomasz Wasilczyk0f1776d2017-08-03 11:03:49 -0700366 * A map of vendor-specific opaque strings, passed from HAL without changes.
367 * Format of these strings can vary across vendors.
Tomasz Wasilczyk6849aa82017-03-29 11:59:23 -0700368 *
369 * It may be used for extra features, that's not supported by a platform,
Tomasz Wasilczyk0f1776d2017-08-03 11:03:49 -0700370 * for example: preset-slots=6; ultra-hd-capable=false.
Tomasz Wasilczyk6849aa82017-03-29 11:59:23 -0700371 *
Tomasz Wasilczyk0f1776d2017-08-03 11:03:49 -0700372 * Keys must be prefixed with unique vendor Java-style namespace,
373 * eg. 'com.somecompany.parameter1'.
Tomasz Wasilczyk6849aa82017-03-29 11:59:23 -0700374 */
Tomasz Wasilczyk0f1776d2017-08-03 11:03:49 -0700375 public @NonNull Map<String, String> getVendorInfo() {
376 return mVendorInfo;
Tomasz Wasilczyke597ce12017-03-24 13:50:53 -0700377 }
378
Eric Laurent2035ac82015-03-05 15:18:44 -0800379 /** List of descriptors for all bands supported by this module.
380 * @return an array of {@link BandDescriptor}.
381 */
382 public BandDescriptor[] getBands() {
383 return mBands;
384 }
385
386 private ModuleProperties(Parcel in) {
387 mId = in.readInt();
Tomasz Wasilczykd15c9df2017-06-13 10:10:36 -0700388 String serviceName = in.readString();
389 mServiceName = TextUtils.isEmpty(serviceName) ? "default" : serviceName;
Eric Laurent2035ac82015-03-05 15:18:44 -0800390 mClassId = in.readInt();
391 mImplementor = in.readString();
392 mProduct = in.readString();
393 mVersion = in.readString();
394 mSerial = in.readString();
395 mNumTuners = in.readInt();
396 mNumAudioSources = in.readInt();
397 mIsCaptureSupported = in.readInt() == 1;
398 Parcelable[] tmp = in.readParcelableArray(BandDescriptor.class.getClassLoader());
399 mBands = new BandDescriptor[tmp.length];
400 for (int i = 0; i < tmp.length; i++) {
401 mBands[i] = (BandDescriptor) tmp[i];
402 }
Tomasz Wasilczyk6849aa82017-03-29 11:59:23 -0700403 mIsBgScanSupported = in.readInt() == 1;
Tomasz Wasilczyk54587ce2017-07-16 15:15:40 -0700404 mSupportedProgramTypes = arrayToSet(in.createIntArray());
405 mSupportedIdentifierTypes = arrayToSet(in.createIntArray());
Tomasz Wasilczyk436128f2018-01-08 16:46:09 -0800406 mVendorInfo = Utils.readStringMap(in);
Eric Laurent2035ac82015-03-05 15:18:44 -0800407 }
408
409 public static final Parcelable.Creator<ModuleProperties> CREATOR
410 = new Parcelable.Creator<ModuleProperties>() {
411 public ModuleProperties createFromParcel(Parcel in) {
412 return new ModuleProperties(in);
413 }
414
415 public ModuleProperties[] newArray(int size) {
416 return new ModuleProperties[size];
417 }
418 };
419
420 @Override
421 public void writeToParcel(Parcel dest, int flags) {
422 dest.writeInt(mId);
Tomasz Wasilczykd15c9df2017-06-13 10:10:36 -0700423 dest.writeString(mServiceName);
Eric Laurent2035ac82015-03-05 15:18:44 -0800424 dest.writeInt(mClassId);
425 dest.writeString(mImplementor);
426 dest.writeString(mProduct);
427 dest.writeString(mVersion);
428 dest.writeString(mSerial);
429 dest.writeInt(mNumTuners);
430 dest.writeInt(mNumAudioSources);
431 dest.writeInt(mIsCaptureSupported ? 1 : 0);
432 dest.writeParcelableArray(mBands, flags);
Tomasz Wasilczyk6849aa82017-03-29 11:59:23 -0700433 dest.writeInt(mIsBgScanSupported ? 1 : 0);
Tomasz Wasilczyk54587ce2017-07-16 15:15:40 -0700434 dest.writeIntArray(setToArray(mSupportedProgramTypes));
435 dest.writeIntArray(setToArray(mSupportedIdentifierTypes));
Tomasz Wasilczyk436128f2018-01-08 16:46:09 -0800436 Utils.writeStringMap(dest, mVendorInfo);
Eric Laurent2035ac82015-03-05 15:18:44 -0800437 }
438
439 @Override
440 public int describeContents() {
441 return 0;
442 }
443
444 @Override
445 public String toString() {
Tomasz Wasilczykd15c9df2017-06-13 10:10:36 -0700446 return "ModuleProperties [mId=" + mId
447 + ", mServiceName=" + mServiceName + ", mClassId=" + mClassId
Eric Laurent2035ac82015-03-05 15:18:44 -0800448 + ", mImplementor=" + mImplementor + ", mProduct=" + mProduct
449 + ", mVersion=" + mVersion + ", mSerial=" + mSerial
450 + ", mNumTuners=" + mNumTuners
451 + ", mNumAudioSources=" + mNumAudioSources
452 + ", mIsCaptureSupported=" + mIsCaptureSupported
Tomasz Wasilczyk6849aa82017-03-29 11:59:23 -0700453 + ", mIsBgScanSupported=" + mIsBgScanSupported
Eric Laurent2035ac82015-03-05 15:18:44 -0800454 + ", mBands=" + Arrays.toString(mBands) + "]";
455 }
456
457 @Override
458 public int hashCode() {
459 final int prime = 31;
460 int result = 1;
461 result = prime * result + mId;
Tomasz Wasilczykd15c9df2017-06-13 10:10:36 -0700462 result = prime * result + mServiceName.hashCode();
Eric Laurent2035ac82015-03-05 15:18:44 -0800463 result = prime * result + mClassId;
464 result = prime * result + ((mImplementor == null) ? 0 : mImplementor.hashCode());
465 result = prime * result + ((mProduct == null) ? 0 : mProduct.hashCode());
466 result = prime * result + ((mVersion == null) ? 0 : mVersion.hashCode());
467 result = prime * result + ((mSerial == null) ? 0 : mSerial.hashCode());
468 result = prime * result + mNumTuners;
469 result = prime * result + mNumAudioSources;
470 result = prime * result + (mIsCaptureSupported ? 1 : 0);
471 result = prime * result + Arrays.hashCode(mBands);
Tomasz Wasilczyk6849aa82017-03-29 11:59:23 -0700472 result = prime * result + (mIsBgScanSupported ? 1 : 0);
Tomasz Wasilczyk0f1776d2017-08-03 11:03:49 -0700473 result = prime * result + mVendorInfo.hashCode();
Eric Laurent2035ac82015-03-05 15:18:44 -0800474 return result;
475 }
476
477 @Override
478 public boolean equals(Object obj) {
479 if (this == obj)
480 return true;
481 if (!(obj instanceof ModuleProperties))
482 return false;
483 ModuleProperties other = (ModuleProperties) obj;
484 if (mId != other.getId())
485 return false;
Tomasz Wasilczykd15c9df2017-06-13 10:10:36 -0700486 if (!TextUtils.equals(mServiceName, other.mServiceName)) return false;
Eric Laurent2035ac82015-03-05 15:18:44 -0800487 if (mClassId != other.getClassId())
488 return false;
489 if (mImplementor == null) {
490 if (other.getImplementor() != null)
491 return false;
492 } else if (!mImplementor.equals(other.getImplementor()))
493 return false;
494 if (mProduct == null) {
495 if (other.getProduct() != null)
496 return false;
497 } else if (!mProduct.equals(other.getProduct()))
498 return false;
499 if (mVersion == null) {
500 if (other.getVersion() != null)
501 return false;
502 } else if (!mVersion.equals(other.getVersion()))
503 return false;
504 if (mSerial == null) {
505 if (other.getSerial() != null)
506 return false;
507 } else if (!mSerial.equals(other.getSerial()))
508 return false;
509 if (mNumTuners != other.getNumTuners())
510 return false;
511 if (mNumAudioSources != other.getNumAudioSources())
512 return false;
513 if (mIsCaptureSupported != other.isCaptureSupported())
514 return false;
515 if (!Arrays.equals(mBands, other.getBands()))
516 return false;
Tomasz Wasilczyk6849aa82017-03-29 11:59:23 -0700517 if (mIsBgScanSupported != other.isBackgroundScanningSupported())
518 return false;
Tomasz Wasilczyk0f1776d2017-08-03 11:03:49 -0700519 if (!mVendorInfo.equals(other.mVendorInfo)) return false;
Eric Laurent2035ac82015-03-05 15:18:44 -0800520 return true;
521 }
522 }
523
524 /** Radio band descriptor: an element in ModuleProperties bands array.
525 * It is either an instance of {@link FmBandDescriptor} or {@link AmBandDescriptor} */
526 public static class BandDescriptor implements Parcelable {
527
528 private final int mRegion;
529 private final int mType;
530 private final int mLowerLimit;
531 private final int mUpperLimit;
532 private final int mSpacing;
533
534 BandDescriptor(int region, int type, int lowerLimit, int upperLimit, int spacing) {
Tomasz Wasilczykf24ecf72017-04-24 13:32:32 -0700535 if (type != BAND_AM && type != BAND_FM && type != BAND_FM_HD && type != BAND_AM_HD) {
536 throw new IllegalArgumentException("Unsupported band: " + type);
537 }
Eric Laurent2035ac82015-03-05 15:18:44 -0800538 mRegion = region;
539 mType = type;
540 mLowerLimit = lowerLimit;
541 mUpperLimit = upperLimit;
542 mSpacing = spacing;
543 }
544
545 /** Region this band applies to. E.g. {@link #REGION_ITU_1}
546 * @return the region this band is associated to.
547 */
548 public int getRegion() {
549 return mRegion;
550 }
551 /** Band type, e.g {@link #BAND_FM}. Defines the subclass this descriptor can be cast to:
552 * <ul>
553 * <li>{@link #BAND_FM} or {@link #BAND_FM_HD} cast to {@link FmBandDescriptor}, </li>
554 * <li>{@link #BAND_AM} cast to {@link AmBandDescriptor}, </li>
555 * </ul>
556 * @return the band type.
557 */
558 public int getType() {
559 return mType;
560 }
Tomasz Wasilczyk2880b9a2017-06-28 14:28:47 -0700561
562 /**
563 * Checks if the band is either AM or AM_HD.
564 *
565 * @return {@code true}, if band is AM or AM_HD.
566 */
567 public boolean isAmBand() {
568 return mType == BAND_AM || mType == BAND_AM_HD;
569 }
570
571 /**
572 * Checks if the band is either FM or FM_HD.
573 *
574 * @return {@code true}, if band is FM or FM_HD.
575 */
576 public boolean isFmBand() {
577 return mType == BAND_FM || mType == BAND_FM_HD;
578 }
579
Eric Laurent2035ac82015-03-05 15:18:44 -0800580 /** Lower band limit expressed in units according to band type.
581 * Currently all defined band types express channels as frequency in kHz
582 * @return the lower band limit.
583 */
584 public int getLowerLimit() {
585 return mLowerLimit;
586 }
587 /** Upper band limit expressed in units according to band type.
588 * Currently all defined band types express channels as frequency in kHz
589 * @return the upper band limit.
590 */
591 public int getUpperLimit() {
592 return mUpperLimit;
593 }
594 /** Channel spacing in units according to band type.
595 * Currently all defined band types express channels as frequency in kHz
596 * @return the channel spacing.
597 */
598 public int getSpacing() {
599 return mSpacing;
600 }
601
602 private BandDescriptor(Parcel in) {
603 mRegion = in.readInt();
604 mType = in.readInt();
605 mLowerLimit = in.readInt();
606 mUpperLimit = in.readInt();
607 mSpacing = in.readInt();
608 }
609
Tomasz Wasilczykf24ecf72017-04-24 13:32:32 -0700610 private static int lookupTypeFromParcel(Parcel in) {
611 int pos = in.dataPosition();
612 in.readInt(); // skip region
613 int type = in.readInt();
614 in.setDataPosition(pos);
615 return type;
616 }
617
Eric Laurent2035ac82015-03-05 15:18:44 -0800618 public static final Parcelable.Creator<BandDescriptor> CREATOR
619 = new Parcelable.Creator<BandDescriptor>() {
620 public BandDescriptor createFromParcel(Parcel in) {
Tomasz Wasilczykf24ecf72017-04-24 13:32:32 -0700621 int type = lookupTypeFromParcel(in);
622 switch (type) {
623 case BAND_FM:
624 case BAND_FM_HD:
625 return new FmBandDescriptor(in);
626 case BAND_AM:
627 case BAND_AM_HD:
628 return new AmBandDescriptor(in);
629 default:
630 throw new IllegalArgumentException("Unsupported band: " + type);
631 }
Eric Laurent2035ac82015-03-05 15:18:44 -0800632 }
633
634 public BandDescriptor[] newArray(int size) {
635 return new BandDescriptor[size];
636 }
637 };
638
639 @Override
640 public void writeToParcel(Parcel dest, int flags) {
641 dest.writeInt(mRegion);
642 dest.writeInt(mType);
643 dest.writeInt(mLowerLimit);
644 dest.writeInt(mUpperLimit);
645 dest.writeInt(mSpacing);
646 }
647
648 @Override
649 public int describeContents() {
650 return 0;
651 }
652
653 @Override
654 public String toString() {
655 return "BandDescriptor [mRegion=" + mRegion + ", mType=" + mType + ", mLowerLimit="
656 + mLowerLimit + ", mUpperLimit=" + mUpperLimit + ", mSpacing=" + mSpacing + "]";
657 }
658
659 @Override
660 public int hashCode() {
661 final int prime = 31;
662 int result = 1;
663 result = prime * result + mRegion;
664 result = prime * result + mType;
665 result = prime * result + mLowerLimit;
666 result = prime * result + mUpperLimit;
667 result = prime * result + mSpacing;
668 return result;
669 }
670
671 @Override
672 public boolean equals(Object obj) {
673 if (this == obj)
674 return true;
675 if (!(obj instanceof BandDescriptor))
676 return false;
677 BandDescriptor other = (BandDescriptor) obj;
678 if (mRegion != other.getRegion())
679 return false;
680 if (mType != other.getType())
681 return false;
682 if (mLowerLimit != other.getLowerLimit())
683 return false;
684 if (mUpperLimit != other.getUpperLimit())
685 return false;
686 if (mSpacing != other.getSpacing())
687 return false;
688 return true;
689 }
690 }
691
692 /** FM band descriptor
693 * @see #BAND_FM
694 * @see #BAND_FM_HD */
695 public static class FmBandDescriptor extends BandDescriptor {
696 private final boolean mStereo;
697 private final boolean mRds;
698 private final boolean mTa;
699 private final boolean mAf;
Sanket Agarwal7058e4c2015-10-08 14:14:20 -0700700 private final boolean mEa;
Eric Laurent2035ac82015-03-05 15:18:44 -0800701
Tomasz Wasilczykf58305d2017-12-28 14:03:15 -0800702 /** @hide */
703 public FmBandDescriptor(int region, int type, int lowerLimit, int upperLimit, int spacing,
Sanket Agarwal7058e4c2015-10-08 14:14:20 -0700704 boolean stereo, boolean rds, boolean ta, boolean af, boolean ea) {
Eric Laurent2035ac82015-03-05 15:18:44 -0800705 super(region, type, lowerLimit, upperLimit, spacing);
706 mStereo = stereo;
707 mRds = rds;
708 mTa = ta;
709 mAf = af;
Sanket Agarwal7058e4c2015-10-08 14:14:20 -0700710 mEa = ea;
Eric Laurent2035ac82015-03-05 15:18:44 -0800711 }
712
713 /** Stereo is supported
714 * @return {@code true} if stereo is supported, {@code false} otherwise.
715 */
716 public boolean isStereoSupported() {
717 return mStereo;
718 }
719 /** RDS or RBDS(if region is ITU2) is supported
720 * @return {@code true} if RDS or RBDS is supported, {@code false} otherwise.
721 */
722 public boolean isRdsSupported() {
723 return mRds;
724 }
725 /** Traffic announcement is supported
726 * @return {@code true} if TA is supported, {@code false} otherwise.
727 */
728 public boolean isTaSupported() {
729 return mTa;
730 }
731 /** Alternate Frequency Switching is supported
732 * @return {@code true} if AF switching is supported, {@code false} otherwise.
733 */
734 public boolean isAfSupported() {
735 return mAf;
736 }
737
Sanket Agarwal7058e4c2015-10-08 14:14:20 -0700738 /** Emergency Announcement is supported
739 * @return {@code true} if Emergency annoucement is supported, {@code false} otherwise.
740 */
741 public boolean isEaSupported() {
742 return mEa;
743 }
744
Eric Laurent2035ac82015-03-05 15:18:44 -0800745 /* Parcelable implementation */
746 private FmBandDescriptor(Parcel in) {
747 super(in);
748 mStereo = in.readByte() == 1;
749 mRds = in.readByte() == 1;
750 mTa = in.readByte() == 1;
751 mAf = in.readByte() == 1;
Sanket Agarwal7058e4c2015-10-08 14:14:20 -0700752 mEa = in.readByte() == 1;
Eric Laurent2035ac82015-03-05 15:18:44 -0800753 }
754
755 public static final Parcelable.Creator<FmBandDescriptor> CREATOR
756 = new Parcelable.Creator<FmBandDescriptor>() {
757 public FmBandDescriptor createFromParcel(Parcel in) {
758 return new FmBandDescriptor(in);
759 }
760
761 public FmBandDescriptor[] newArray(int size) {
762 return new FmBandDescriptor[size];
763 }
764 };
765
766 @Override
767 public void writeToParcel(Parcel dest, int flags) {
768 super.writeToParcel(dest, flags);
769 dest.writeByte((byte) (mStereo ? 1 : 0));
770 dest.writeByte((byte) (mRds ? 1 : 0));
771 dest.writeByte((byte) (mTa ? 1 : 0));
772 dest.writeByte((byte) (mAf ? 1 : 0));
Sanket Agarwal7058e4c2015-10-08 14:14:20 -0700773 dest.writeByte((byte) (mEa ? 1 : 0));
Eric Laurent2035ac82015-03-05 15:18:44 -0800774 }
775
776 @Override
777 public int describeContents() {
778 return 0;
779 }
780
781 @Override
782 public String toString() {
783 return "FmBandDescriptor [ "+ super.toString() + " mStereo=" + mStereo
Sanket Agarwal7058e4c2015-10-08 14:14:20 -0700784 + ", mRds=" + mRds + ", mTa=" + mTa + ", mAf=" + mAf +
785 ", mEa =" + mEa + "]";
Eric Laurent2035ac82015-03-05 15:18:44 -0800786 }
787
788 @Override
789 public int hashCode() {
790 final int prime = 31;
791 int result = super.hashCode();
792 result = prime * result + (mStereo ? 1 : 0);
793 result = prime * result + (mRds ? 1 : 0);
794 result = prime * result + (mTa ? 1 : 0);
795 result = prime * result + (mAf ? 1 : 0);
Sanket Agarwal7058e4c2015-10-08 14:14:20 -0700796 result = prime * result + (mEa ? 1 : 0);
Eric Laurent2035ac82015-03-05 15:18:44 -0800797 return result;
798 }
799
800 @Override
801 public boolean equals(Object obj) {
802 if (this == obj)
803 return true;
804 if (!super.equals(obj))
805 return false;
806 if (!(obj instanceof FmBandDescriptor))
807 return false;
808 FmBandDescriptor other = (FmBandDescriptor) obj;
809 if (mStereo != other.isStereoSupported())
810 return false;
811 if (mRds != other.isRdsSupported())
812 return false;
813 if (mTa != other.isTaSupported())
814 return false;
815 if (mAf != other.isAfSupported())
816 return false;
Sanket Agarwal7058e4c2015-10-08 14:14:20 -0700817 if (mEa != other.isEaSupported())
818 return false;
Eric Laurent2035ac82015-03-05 15:18:44 -0800819 return true;
820 }
821 }
822
823 /** AM band descriptor.
824 * @see #BAND_AM */
825 public static class AmBandDescriptor extends BandDescriptor {
826
827 private final boolean mStereo;
828
Tomasz Wasilczykf58305d2017-12-28 14:03:15 -0800829 /** @hide */
830 public AmBandDescriptor(int region, int type, int lowerLimit, int upperLimit, int spacing,
Eric Laurent2035ac82015-03-05 15:18:44 -0800831 boolean stereo) {
832 super(region, type, lowerLimit, upperLimit, spacing);
833 mStereo = stereo;
834 }
835
836 /** Stereo is supported
837 * @return {@code true} if stereo is supported, {@code false} otherwise.
838 */
839 public boolean isStereoSupported() {
840 return mStereo;
841 }
842
843 private AmBandDescriptor(Parcel in) {
844 super(in);
845 mStereo = in.readByte() == 1;
846 }
847
848 public static final Parcelable.Creator<AmBandDescriptor> CREATOR
849 = new Parcelable.Creator<AmBandDescriptor>() {
850 public AmBandDescriptor createFromParcel(Parcel in) {
851 return new AmBandDescriptor(in);
852 }
853
854 public AmBandDescriptor[] newArray(int size) {
855 return new AmBandDescriptor[size];
856 }
857 };
858
859 @Override
860 public void writeToParcel(Parcel dest, int flags) {
861 super.writeToParcel(dest, flags);
862 dest.writeByte((byte) (mStereo ? 1 : 0));
863 }
864
865 @Override
866 public int describeContents() {
867 return 0;
868 }
869
870 @Override
871 public String toString() {
872 return "AmBandDescriptor [ "+ super.toString() + " mStereo=" + mStereo + "]";
873 }
874
875 @Override
876 public int hashCode() {
877 final int prime = 31;
878 int result = super.hashCode();
879 result = prime * result + (mStereo ? 1 : 0);
880 return result;
881 }
882
883 @Override
884 public boolean equals(Object obj) {
885 if (this == obj)
886 return true;
887 if (!super.equals(obj))
888 return false;
889 if (!(obj instanceof AmBandDescriptor))
890 return false;
891 AmBandDescriptor other = (AmBandDescriptor) obj;
892 if (mStereo != other.isStereoSupported())
893 return false;
894 return true;
895 }
896 }
897
898
899 /** Radio band configuration. */
900 public static class BandConfig implements Parcelable {
901
Tomasz Wasilczykf58305d2017-12-28 14:03:15 -0800902 @NonNull final BandDescriptor mDescriptor;
Eric Laurent2035ac82015-03-05 15:18:44 -0800903
904 BandConfig(BandDescriptor descriptor) {
Tomasz Wasilczykf58305d2017-12-28 14:03:15 -0800905 mDescriptor = Objects.requireNonNull(descriptor);
Eric Laurent2035ac82015-03-05 15:18:44 -0800906 }
907
908 BandConfig(int region, int type, int lowerLimit, int upperLimit, int spacing) {
909 mDescriptor = new BandDescriptor(region, type, lowerLimit, upperLimit, spacing);
910 }
911
912 private BandConfig(Parcel in) {
913 mDescriptor = new BandDescriptor(in);
914 }
915
916 BandDescriptor getDescriptor() {
917 return mDescriptor;
918 }
919
920 /** Region this band applies to. E.g. {@link #REGION_ITU_1}
921 * @return the region associated with this band.
922 */
923 public int getRegion() {
924 return mDescriptor.getRegion();
925 }
926 /** Band type, e.g {@link #BAND_FM}. Defines the subclass this descriptor can be cast to:
927 * <ul>
928 * <li>{@link #BAND_FM} or {@link #BAND_FM_HD} cast to {@link FmBandDescriptor}, </li>
929 * <li>{@link #BAND_AM} cast to {@link AmBandDescriptor}, </li>
930 * </ul>
931 * @return the band type.
932 */
933 public int getType() {
934 return mDescriptor.getType();
935 }
936 /** Lower band limit expressed in units according to band type.
937 * Currently all defined band types express channels as frequency in kHz
938 * @return the lower band limit.
939 */
940 public int getLowerLimit() {
941 return mDescriptor.getLowerLimit();
942 }
943 /** Upper band limit expressed in units according to band type.
944 * Currently all defined band types express channels as frequency in kHz
945 * @return the upper band limit.
946 */
947 public int getUpperLimit() {
948 return mDescriptor.getUpperLimit();
949 }
950 /** Channel spacing in units according to band type.
951 * Currently all defined band types express channels as frequency in kHz
952 * @return the channel spacing.
953 */
954 public int getSpacing() {
955 return mDescriptor.getSpacing();
956 }
957
958
959 public static final Parcelable.Creator<BandConfig> CREATOR
960 = new Parcelable.Creator<BandConfig>() {
961 public BandConfig createFromParcel(Parcel in) {
Tomasz Wasilczykf24ecf72017-04-24 13:32:32 -0700962 int type = BandDescriptor.lookupTypeFromParcel(in);
963 switch (type) {
964 case BAND_FM:
965 case BAND_FM_HD:
966 return new FmBandConfig(in);
967 case BAND_AM:
968 case BAND_AM_HD:
969 return new AmBandConfig(in);
970 default:
971 throw new IllegalArgumentException("Unsupported band: " + type);
972 }
Eric Laurent2035ac82015-03-05 15:18:44 -0800973 }
974
975 public BandConfig[] newArray(int size) {
976 return new BandConfig[size];
977 }
978 };
979
980 @Override
981 public void writeToParcel(Parcel dest, int flags) {
982 mDescriptor.writeToParcel(dest, flags);
983 }
984
985 @Override
986 public int describeContents() {
987 return 0;
988 }
989
990 @Override
991 public String toString() {
992 return "BandConfig [ " + mDescriptor.toString() + "]";
993 }
994
995 @Override
996 public int hashCode() {
997 final int prime = 31;
998 int result = 1;
999 result = prime * result + mDescriptor.hashCode();
1000 return result;
1001 }
1002
1003 @Override
1004 public boolean equals(Object obj) {
1005 if (this == obj)
1006 return true;
1007 if (!(obj instanceof BandConfig))
1008 return false;
1009 BandConfig other = (BandConfig) obj;
Tomasz Wasilczykdd767062017-04-28 08:19:34 -07001010 BandDescriptor otherDesc = other.getDescriptor();
1011 if ((mDescriptor == null) != (otherDesc == null)) return false;
1012 if (mDescriptor != null && !mDescriptor.equals(otherDesc)) return false;
Eric Laurent2035ac82015-03-05 15:18:44 -08001013 return true;
1014 }
1015 }
1016
1017 /** FM band configuration.
1018 * @see #BAND_FM
1019 * @see #BAND_FM_HD */
1020 public static class FmBandConfig extends BandConfig {
1021 private final boolean mStereo;
1022 private final boolean mRds;
1023 private final boolean mTa;
1024 private final boolean mAf;
Sanket Agarwal7058e4c2015-10-08 14:14:20 -07001025 private final boolean mEa;
Eric Laurent2035ac82015-03-05 15:18:44 -08001026
Tomasz Wasilczykf58305d2017-12-28 14:03:15 -08001027 /** @hide */
1028 public FmBandConfig(FmBandDescriptor descriptor) {
Eric Laurent2035ac82015-03-05 15:18:44 -08001029 super((BandDescriptor)descriptor);
1030 mStereo = descriptor.isStereoSupported();
1031 mRds = descriptor.isRdsSupported();
1032 mTa = descriptor.isTaSupported();
1033 mAf = descriptor.isAfSupported();
Sanket Agarwal7058e4c2015-10-08 14:14:20 -07001034 mEa = descriptor.isEaSupported();
Eric Laurent2035ac82015-03-05 15:18:44 -08001035 }
1036
1037 FmBandConfig(int region, int type, int lowerLimit, int upperLimit, int spacing,
Sanket Agarwal7058e4c2015-10-08 14:14:20 -07001038 boolean stereo, boolean rds, boolean ta, boolean af, boolean ea) {
Eric Laurent2035ac82015-03-05 15:18:44 -08001039 super(region, type, lowerLimit, upperLimit, spacing);
1040 mStereo = stereo;
1041 mRds = rds;
1042 mTa = ta;
1043 mAf = af;
Sanket Agarwal7058e4c2015-10-08 14:14:20 -07001044 mEa = ea;
Eric Laurent2035ac82015-03-05 15:18:44 -08001045 }
1046
1047 /** Get stereo enable state
1048 * @return the enable state.
1049 */
1050 public boolean getStereo() {
1051 return mStereo;
1052 }
1053
1054 /** Get RDS or RBDS(if region is ITU2) enable state
1055 * @return the enable state.
1056 */
1057 public boolean getRds() {
1058 return mRds;
1059 }
1060
1061 /** Get Traffic announcement enable state
1062 * @return the enable state.
1063 */
1064 public boolean getTa() {
1065 return mTa;
1066 }
1067
1068 /** Get Alternate Frequency Switching enable state
1069 * @return the enable state.
1070 */
1071 public boolean getAf() {
1072 return mAf;
1073 }
1074
Sanket Agarwal7058e4c2015-10-08 14:14:20 -07001075 /**
1076 * Get Emergency announcement enable state
1077 * @return the enable state.
1078 */
1079 public boolean getEa() {
1080 return mEa;
1081 }
1082
Eric Laurent2035ac82015-03-05 15:18:44 -08001083 private FmBandConfig(Parcel in) {
1084 super(in);
1085 mStereo = in.readByte() == 1;
1086 mRds = in.readByte() == 1;
1087 mTa = in.readByte() == 1;
1088 mAf = in.readByte() == 1;
Sanket Agarwal7058e4c2015-10-08 14:14:20 -07001089 mEa = in.readByte() == 1;
Eric Laurent2035ac82015-03-05 15:18:44 -08001090 }
1091
1092 public static final Parcelable.Creator<FmBandConfig> CREATOR
1093 = new Parcelable.Creator<FmBandConfig>() {
1094 public FmBandConfig createFromParcel(Parcel in) {
1095 return new FmBandConfig(in);
1096 }
1097
1098 public FmBandConfig[] newArray(int size) {
1099 return new FmBandConfig[size];
1100 }
1101 };
1102
1103 @Override
1104 public void writeToParcel(Parcel dest, int flags) {
1105 super.writeToParcel(dest, flags);
1106 dest.writeByte((byte) (mStereo ? 1 : 0));
1107 dest.writeByte((byte) (mRds ? 1 : 0));
1108 dest.writeByte((byte) (mTa ? 1 : 0));
1109 dest.writeByte((byte) (mAf ? 1 : 0));
Sanket Agarwal7058e4c2015-10-08 14:14:20 -07001110 dest.writeByte((byte) (mEa ? 1 : 0));
Eric Laurent2035ac82015-03-05 15:18:44 -08001111 }
1112
1113 @Override
1114 public int describeContents() {
1115 return 0;
1116 }
1117
1118 @Override
1119 public String toString() {
1120 return "FmBandConfig [" + super.toString()
1121 + ", mStereo=" + mStereo + ", mRds=" + mRds + ", mTa=" + mTa
Sanket Agarwal7058e4c2015-10-08 14:14:20 -07001122 + ", mAf=" + mAf + ", mEa =" + mEa + "]";
Eric Laurent2035ac82015-03-05 15:18:44 -08001123 }
1124
1125 @Override
1126 public int hashCode() {
1127 final int prime = 31;
1128 int result = super.hashCode();
1129 result = prime * result + (mStereo ? 1 : 0);
1130 result = prime * result + (mRds ? 1 : 0);
1131 result = prime * result + (mTa ? 1 : 0);
1132 result = prime * result + (mAf ? 1 : 0);
Sanket Agarwal7058e4c2015-10-08 14:14:20 -07001133 result = prime * result + (mEa ? 1 : 0);
Eric Laurent2035ac82015-03-05 15:18:44 -08001134 return result;
1135 }
1136
1137 @Override
1138 public boolean equals(Object obj) {
1139 if (this == obj)
1140 return true;
1141 if (!super.equals(obj))
1142 return false;
1143 if (!(obj instanceof FmBandConfig))
1144 return false;
1145 FmBandConfig other = (FmBandConfig) obj;
1146 if (mStereo != other.mStereo)
1147 return false;
1148 if (mRds != other.mRds)
1149 return false;
1150 if (mTa != other.mTa)
1151 return false;
1152 if (mAf != other.mAf)
1153 return false;
Sanket Agarwal7058e4c2015-10-08 14:14:20 -07001154 if (mEa != other.mEa)
1155 return false;
Eric Laurent2035ac82015-03-05 15:18:44 -08001156 return true;
1157 }
1158
1159 /**
1160 * Builder class for {@link FmBandConfig} objects.
1161 */
1162 public static class Builder {
1163 private final BandDescriptor mDescriptor;
1164 private boolean mStereo;
1165 private boolean mRds;
1166 private boolean mTa;
1167 private boolean mAf;
Sanket Agarwal7058e4c2015-10-08 14:14:20 -07001168 private boolean mEa;
Eric Laurent2035ac82015-03-05 15:18:44 -08001169
1170 /**
1171 * Constructs a new Builder with the defaults from an {@link FmBandDescriptor} .
1172 * @param descriptor the FmBandDescriptor defaults are read from .
1173 */
1174 public Builder(FmBandDescriptor descriptor) {
1175 mDescriptor = new BandDescriptor(descriptor.getRegion(), descriptor.getType(),
1176 descriptor.getLowerLimit(), descriptor.getUpperLimit(),
1177 descriptor.getSpacing());
1178 mStereo = descriptor.isStereoSupported();
1179 mRds = descriptor.isRdsSupported();
1180 mTa = descriptor.isTaSupported();
1181 mAf = descriptor.isAfSupported();
Sanket Agarwal7058e4c2015-10-08 14:14:20 -07001182 mEa = descriptor.isEaSupported();
Eric Laurent2035ac82015-03-05 15:18:44 -08001183 }
1184
1185 /**
1186 * Constructs a new Builder from a given {@link FmBandConfig}
1187 * @param config the FmBandConfig object whose data will be reused in the new Builder.
1188 */
1189 public Builder(FmBandConfig config) {
1190 mDescriptor = new BandDescriptor(config.getRegion(), config.getType(),
1191 config.getLowerLimit(), config.getUpperLimit(), config.getSpacing());
1192 mStereo = config.getStereo();
1193 mRds = config.getRds();
1194 mTa = config.getTa();
1195 mAf = config.getAf();
Sanket Agarwal7058e4c2015-10-08 14:14:20 -07001196 mEa = config.getEa();
Eric Laurent2035ac82015-03-05 15:18:44 -08001197 }
1198
1199 /**
1200 * Combines all of the parameters that have been set and return a new
1201 * {@link FmBandConfig} object.
1202 * @return a new {@link FmBandConfig} object
1203 */
1204 public FmBandConfig build() {
1205 FmBandConfig config = new FmBandConfig(mDescriptor.getRegion(),
1206 mDescriptor.getType(), mDescriptor.getLowerLimit(),
1207 mDescriptor.getUpperLimit(), mDescriptor.getSpacing(),
Sanket Agarwal7058e4c2015-10-08 14:14:20 -07001208 mStereo, mRds, mTa, mAf, mEa);
Eric Laurent2035ac82015-03-05 15:18:44 -08001209 return config;
1210 }
1211
1212 /** Set stereo enable state
1213 * @param state The new enable state.
1214 * @return the same Builder instance.
1215 */
1216 public Builder setStereo(boolean state) {
1217 mStereo = state;
1218 return this;
1219 }
1220
1221 /** Set RDS or RBDS(if region is ITU2) enable state
1222 * @param state The new enable state.
1223 * @return the same Builder instance.
1224 */
1225 public Builder setRds(boolean state) {
1226 mRds = state;
1227 return this;
1228 }
1229
1230 /** Set Traffic announcement enable state
1231 * @param state The new enable state.
1232 * @return the same Builder instance.
1233 */
1234 public Builder setTa(boolean state) {
1235 mTa = state;
1236 return this;
1237 }
1238
1239 /** Set Alternate Frequency Switching enable state
1240 * @param state The new enable state.
1241 * @return the same Builder instance.
1242 */
1243 public Builder setAf(boolean state) {
1244 mAf = state;
1245 return this;
1246 }
Sanket Agarwal7058e4c2015-10-08 14:14:20 -07001247
1248 /** Set Emergency Announcement enable state
1249 * @param state The new enable state.
1250 * @return the same Builder instance.
1251 */
1252 public Builder setEa(boolean state) {
1253 mEa = state;
1254 return this;
1255 }
Eric Laurent2035ac82015-03-05 15:18:44 -08001256 };
1257 }
1258
1259 /** AM band configuration.
1260 * @see #BAND_AM */
1261 public static class AmBandConfig extends BandConfig {
1262 private final boolean mStereo;
1263
Tomasz Wasilczykf58305d2017-12-28 14:03:15 -08001264 /** @hide */
1265 public AmBandConfig(AmBandDescriptor descriptor) {
Eric Laurent2035ac82015-03-05 15:18:44 -08001266 super((BandDescriptor)descriptor);
1267 mStereo = descriptor.isStereoSupported();
1268 }
1269
1270 AmBandConfig(int region, int type, int lowerLimit, int upperLimit, int spacing,
1271 boolean stereo) {
1272 super(region, type, lowerLimit, upperLimit, spacing);
1273 mStereo = stereo;
1274 }
1275
1276 /** Get stereo enable state
1277 * @return the enable state.
1278 */
1279 public boolean getStereo() {
1280 return mStereo;
1281 }
1282
1283 private AmBandConfig(Parcel in) {
1284 super(in);
1285 mStereo = in.readByte() == 1;
1286 }
1287
1288 public static final Parcelable.Creator<AmBandConfig> CREATOR
1289 = new Parcelable.Creator<AmBandConfig>() {
1290 public AmBandConfig createFromParcel(Parcel in) {
1291 return new AmBandConfig(in);
1292 }
1293
1294 public AmBandConfig[] newArray(int size) {
1295 return new AmBandConfig[size];
1296 }
1297 };
1298
1299 @Override
1300 public void writeToParcel(Parcel dest, int flags) {
1301 super.writeToParcel(dest, flags);
1302 dest.writeByte((byte) (mStereo ? 1 : 0));
1303 }
1304
1305 @Override
1306 public int describeContents() {
1307 return 0;
1308 }
1309
1310 @Override
1311 public String toString() {
1312 return "AmBandConfig [" + super.toString()
1313 + ", mStereo=" + mStereo + "]";
1314 }
1315
1316 @Override
1317 public int hashCode() {
1318 final int prime = 31;
1319 int result = super.hashCode();
1320 result = prime * result + (mStereo ? 1 : 0);
1321 return result;
1322 }
1323
1324 @Override
1325 public boolean equals(Object obj) {
1326 if (this == obj)
1327 return true;
1328 if (!super.equals(obj))
1329 return false;
1330 if (!(obj instanceof AmBandConfig))
1331 return false;
1332 AmBandConfig other = (AmBandConfig) obj;
1333 if (mStereo != other.getStereo())
1334 return false;
1335 return true;
1336 }
1337
1338 /**
1339 * Builder class for {@link AmBandConfig} objects.
1340 */
1341 public static class Builder {
1342 private final BandDescriptor mDescriptor;
1343 private boolean mStereo;
1344
1345 /**
1346 * Constructs a new Builder with the defaults from an {@link AmBandDescriptor} .
1347 * @param descriptor the FmBandDescriptor defaults are read from .
1348 */
1349 public Builder(AmBandDescriptor descriptor) {
1350 mDescriptor = new BandDescriptor(descriptor.getRegion(), descriptor.getType(),
1351 descriptor.getLowerLimit(), descriptor.getUpperLimit(),
1352 descriptor.getSpacing());
1353 mStereo = descriptor.isStereoSupported();
1354 }
1355
1356 /**
1357 * Constructs a new Builder from a given {@link AmBandConfig}
1358 * @param config the FmBandConfig object whose data will be reused in the new Builder.
1359 */
1360 public Builder(AmBandConfig config) {
1361 mDescriptor = new BandDescriptor(config.getRegion(), config.getType(),
1362 config.getLowerLimit(), config.getUpperLimit(), config.getSpacing());
1363 mStereo = config.getStereo();
1364 }
1365
1366 /**
1367 * Combines all of the parameters that have been set and return a new
1368 * {@link AmBandConfig} object.
1369 * @return a new {@link AmBandConfig} object
1370 */
1371 public AmBandConfig build() {
1372 AmBandConfig config = new AmBandConfig(mDescriptor.getRegion(),
1373 mDescriptor.getType(), mDescriptor.getLowerLimit(),
1374 mDescriptor.getUpperLimit(), mDescriptor.getSpacing(),
1375 mStereo);
1376 return config;
1377 }
1378
1379 /** Set stereo enable state
1380 * @param state The new enable state.
1381 * @return the same Builder instance.
1382 */
1383 public Builder setStereo(boolean state) {
1384 mStereo = state;
1385 return this;
1386 }
1387 };
1388 }
1389
Tomasz Wasilczykeab3e552018-01-11 20:12:10 -08001390 /** Radio program information. */
Eric Laurent2035ac82015-03-05 15:18:44 -08001391 public static class ProgramInfo implements Parcelable {
1392
Tomasz Wasilczykeab3e552018-01-11 20:12:10 -08001393 // sourced from hardware/interfaces/broadcastradio/2.0/types.hal
Tomasz Wasilczykc4cd8232017-07-14 10:46:15 -07001394 private static final int FLAG_LIVE = 1 << 0;
1395 private static final int FLAG_MUTED = 1 << 1;
1396 private static final int FLAG_TRAFFIC_PROGRAM = 1 << 2;
1397 private static final int FLAG_TRAFFIC_ANNOUNCEMENT = 1 << 3;
Tomasz Wasilczykeab3e552018-01-11 20:12:10 -08001398 private static final int FLAG_TUNED = 1 << 4;
1399 private static final int FLAG_STEREO = 1 << 5;
Tomasz Wasilczyk5fb600b2017-03-24 14:55:08 -07001400
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -07001401 @NonNull private final ProgramSelector mSelector;
Tomasz Wasilczykeab3e552018-01-11 20:12:10 -08001402 @Nullable private final ProgramSelector.Identifier mLogicallyTunedTo;
1403 @Nullable private final ProgramSelector.Identifier mPhysicallyTunedTo;
1404 @NonNull private final Collection<ProgramSelector.Identifier> mRelatedContent;
1405 private final int mInfoFlags;
1406 private final int mSignalQuality;
1407 @Nullable private final RadioMetadata mMetadata;
Tomasz Wasilczyk0f1776d2017-08-03 11:03:49 -07001408 @NonNull private final Map<String, String> mVendorInfo;
Eric Laurent2035ac82015-03-05 15:18:44 -08001409
Tomasz Wasilczyk436128f2018-01-08 16:46:09 -08001410 /** @hide */
Tomasz Wasilczykeab3e552018-01-11 20:12:10 -08001411 public ProgramInfo(@NonNull ProgramSelector selector,
1412 @Nullable ProgramSelector.Identifier logicallyTunedTo,
1413 @Nullable ProgramSelector.Identifier physicallyTunedTo,
1414 @Nullable Collection<ProgramSelector.Identifier> relatedContent,
1415 int infoFlags, int signalQuality, @Nullable RadioMetadata metadata,
1416 @Nullable Map<String, String> vendorInfo) {
1417 mSelector = Objects.requireNonNull(selector);
1418 mLogicallyTunedTo = logicallyTunedTo;
1419 mPhysicallyTunedTo = physicallyTunedTo;
1420 if (relatedContent == null) {
1421 mRelatedContent = Collections.emptyList();
1422 } else {
1423 Preconditions.checkCollectionElementsNotNull(relatedContent, "relatedContent");
1424 mRelatedContent = relatedContent;
1425 }
1426 mInfoFlags = infoFlags;
1427 mSignalQuality = signalQuality;
Eric Laurent2035ac82015-03-05 15:18:44 -08001428 mMetadata = metadata;
Tomasz Wasilczyk0f1776d2017-08-03 11:03:49 -07001429 mVendorInfo = (vendorInfo == null) ? new HashMap<>() : vendorInfo;
Eric Laurent2035ac82015-03-05 15:18:44 -08001430 }
1431
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -07001432 /**
1433 * Program selector, necessary for tuning to a program.
1434 *
1435 * @return the program selector.
1436 */
1437 public @NonNull ProgramSelector getSelector() {
1438 return mSelector;
1439 }
1440
Tomasz Wasilczykeab3e552018-01-11 20:12:10 -08001441 /**
1442 * Identifier currently used for program selection.
1443 *
1444 * This identifier can be used to determine which technology is
1445 * currently being used for reception.
1446 *
1447 * Some program selectors contain tuning information for different radio
1448 * technologies (i.e. FM RDS and DAB). For example, user may tune using
1449 * a ProgramSelector with RDS_PI primary identifier, but the tuner hardware
1450 * may choose to use DAB technology to make actual tuning. This identifier
1451 * must reflect that.
1452 */
1453 public @Nullable ProgramSelector.Identifier getLogicallyTunedTo() {
1454 return mLogicallyTunedTo;
1455 }
1456
1457 /**
1458 * Identifier currently used by hardware to physically tune to a channel.
1459 *
1460 * Some radio technologies broadcast the same program on multiple channels,
1461 * i.e. with RDS AF the same program may be broadcasted on multiple
1462 * alternative frequencies; the same DAB program may be broadcast on
1463 * multiple ensembles. This identifier points to the channel to which the
1464 * radio hardware is physically tuned to.
1465 */
1466 public @Nullable ProgramSelector.Identifier getPhysicallyTunedTo() {
1467 return mPhysicallyTunedTo;
1468 }
1469
1470 /**
1471 * Primary identifiers of related contents.
1472 *
1473 * Some radio technologies provide pointers to other programs that carry
1474 * related content (i.e. DAB soft-links). This field is a list of pointers
1475 * to other programs on the program list.
1476 *
1477 * Please note, that these identifiers does not have to exist on the program
1478 * list - i.e. DAB tuner may provide information on FM RDS alternatives
1479 * despite not supporting FM RDS. If the system has multiple tuners, another
1480 * one may have it on its list.
1481 */
1482 public @Nullable Collection<ProgramSelector.Identifier> getRelatedContent() {
1483 return mRelatedContent;
1484 }
1485
Eric Laurent2035ac82015-03-05 15:18:44 -08001486 /** Main channel expressed in units according to band type.
1487 * Currently all defined band types express channels as frequency in kHz
1488 * @return the program channel
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -07001489 * @deprecated Use {@link getSelector()} instead.
Eric Laurent2035ac82015-03-05 15:18:44 -08001490 */
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -07001491 @Deprecated
Eric Laurent2035ac82015-03-05 15:18:44 -08001492 public int getChannel() {
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -07001493 try {
1494 return (int) mSelector.getFirstId(ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY);
1495 } catch (IllegalArgumentException ex) {
1496 Log.w(TAG, "Not an AM/FM program");
1497 return 0;
1498 }
Eric Laurent2035ac82015-03-05 15:18:44 -08001499 }
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -07001500
Eric Laurent2035ac82015-03-05 15:18:44 -08001501 /** Sub channel ID. E.g 1 for HD radio HD1
1502 * @return the program sub channel
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -07001503 * @deprecated Use {@link getSelector()} instead.
Eric Laurent2035ac82015-03-05 15:18:44 -08001504 */
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -07001505 @Deprecated
Eric Laurent2035ac82015-03-05 15:18:44 -08001506 public int getSubChannel() {
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -07001507 try {
1508 return (int) mSelector.getFirstId(
1509 ProgramSelector.IDENTIFIER_TYPE_HD_SUBCHANNEL) + 1;
1510 } catch (IllegalArgumentException ex) {
1511 // this is a normal behavior for analog AM/FM selector
1512 return 0;
1513 }
Eric Laurent2035ac82015-03-05 15:18:44 -08001514 }
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -07001515
Eric Laurent2035ac82015-03-05 15:18:44 -08001516 /** {@code true} if the tuner is currently tuned on a valid station
1517 * @return {@code true} if currently tuned, {@code false} otherwise.
1518 */
1519 public boolean isTuned() {
Tomasz Wasilczykeab3e552018-01-11 20:12:10 -08001520 return (mInfoFlags & FLAG_TUNED) != 0;
Eric Laurent2035ac82015-03-05 15:18:44 -08001521 }
Tomasz Wasilczykeab3e552018-01-11 20:12:10 -08001522
Eric Laurent2035ac82015-03-05 15:18:44 -08001523 /** {@code true} if the received program is stereo
1524 * @return {@code true} if stereo, {@code false} otherwise.
1525 */
1526 public boolean isStereo() {
Tomasz Wasilczykeab3e552018-01-11 20:12:10 -08001527 return (mInfoFlags & FLAG_STEREO) != 0;
Eric Laurent2035ac82015-03-05 15:18:44 -08001528 }
Tomasz Wasilczykeab3e552018-01-11 20:12:10 -08001529
Eric Laurent2035ac82015-03-05 15:18:44 -08001530 /** {@code true} if the received program is digital (e.g HD radio)
1531 * @return {@code true} if digital, {@code false} otherwise.
Tomasz Wasilczykeab3e552018-01-11 20:12:10 -08001532 * @deprecated Use {@link getLogicallyTunedTo()} instead.
Eric Laurent2035ac82015-03-05 15:18:44 -08001533 */
Tomasz Wasilczykeab3e552018-01-11 20:12:10 -08001534 @Deprecated
Eric Laurent2035ac82015-03-05 15:18:44 -08001535 public boolean isDigital() {
Tomasz Wasilczykeab3e552018-01-11 20:12:10 -08001536 ProgramSelector.Identifier id = mLogicallyTunedTo;
1537 if (id == null) id = mSelector.getPrimaryId();
1538
1539 int type = id.getType();
1540 return (type != ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY
1541 && type != ProgramSelector.IDENTIFIER_TYPE_RDS_PI);
Eric Laurent2035ac82015-03-05 15:18:44 -08001542 }
Tomasz Wasilczyk5fb600b2017-03-24 14:55:08 -07001543
1544 /**
1545 * {@code true} if the program is currently playing live stream.
1546 * This may result in a slightly altered reception parameters,
1547 * usually targetted at reduced latency.
Tomasz Wasilczyk5fb600b2017-03-24 14:55:08 -07001548 */
1549 public boolean isLive() {
Tomasz Wasilczykeab3e552018-01-11 20:12:10 -08001550 return (mInfoFlags & FLAG_LIVE) != 0;
Tomasz Wasilczyk5fb600b2017-03-24 14:55:08 -07001551 }
1552
1553 /**
1554 * {@code true} if radio stream is not playing, ie. due to bad reception
1555 * conditions or buffering. In this state volume knob MAY be disabled to
1556 * prevent user increasing volume too much.
1557 * It does NOT mean the user has muted audio.
Tomasz Wasilczyk5fb600b2017-03-24 14:55:08 -07001558 */
1559 public boolean isMuted() {
Tomasz Wasilczykeab3e552018-01-11 20:12:10 -08001560 return (mInfoFlags & FLAG_MUTED) != 0;
Tomasz Wasilczyk5fb600b2017-03-24 14:55:08 -07001561 }
1562
Tomasz Wasilczykc4cd8232017-07-14 10:46:15 -07001563 /**
1564 * {@code true} if radio station transmits traffic information
1565 * regularily.
1566 */
1567 public boolean isTrafficProgram() {
Tomasz Wasilczykeab3e552018-01-11 20:12:10 -08001568 return (mInfoFlags & FLAG_TRAFFIC_PROGRAM) != 0;
Tomasz Wasilczykc4cd8232017-07-14 10:46:15 -07001569 }
1570
1571 /**
1572 * {@code true} if radio station transmits traffic information
1573 * at the very moment.
1574 */
1575 public boolean isTrafficAnnouncementActive() {
Tomasz Wasilczykeab3e552018-01-11 20:12:10 -08001576 return (mInfoFlags & FLAG_TRAFFIC_ANNOUNCEMENT) != 0;
Tomasz Wasilczykc4cd8232017-07-14 10:46:15 -07001577 }
1578
Tomasz Wasilczykeab3e552018-01-11 20:12:10 -08001579 /**
1580 * Signal quality (as opposed to the name) indication from 0 (no signal)
1581 * to 100 (excellent)
1582 * @return the signal quality indication.
Eric Laurent2035ac82015-03-05 15:18:44 -08001583 */
1584 public int getSignalStrength() {
Tomasz Wasilczykeab3e552018-01-11 20:12:10 -08001585 return mSignalQuality;
Eric Laurent2035ac82015-03-05 15:18:44 -08001586 }
Tomasz Wasilczykeab3e552018-01-11 20:12:10 -08001587
Eric Laurent2035ac82015-03-05 15:18:44 -08001588 /** Metadata currently received from this station.
1589 * null if no metadata have been received
1590 * @return current meta data received from this program.
1591 */
1592 public RadioMetadata getMetadata() {
1593 return mMetadata;
1594 }
1595
Tomasz Wasilczyk6849aa82017-03-29 11:59:23 -07001596 /**
Tomasz Wasilczyk0f1776d2017-08-03 11:03:49 -07001597 * A map of vendor-specific opaque strings, passed from HAL without changes.
1598 * Format of these strings can vary across vendors.
Tomasz Wasilczyk6849aa82017-03-29 11:59:23 -07001599 *
1600 * It may be used for extra features, that's not supported by a platform,
Tomasz Wasilczyk0f1776d2017-08-03 11:03:49 -07001601 * for example: paid-service=true; bitrate=320kbps.
Tomasz Wasilczyk6849aa82017-03-29 11:59:23 -07001602 *
Tomasz Wasilczyk0f1776d2017-08-03 11:03:49 -07001603 * Keys must be prefixed with unique vendor Java-style namespace,
1604 * eg. 'com.somecompany.parameter1'.
Tomasz Wasilczyk6849aa82017-03-29 11:59:23 -07001605 */
Tomasz Wasilczyk0f1776d2017-08-03 11:03:49 -07001606 public @NonNull Map<String, String> getVendorInfo() {
1607 return mVendorInfo;
Tomasz Wasilczyk6849aa82017-03-29 11:59:23 -07001608 }
1609
Eric Laurent2035ac82015-03-05 15:18:44 -08001610 private ProgramInfo(Parcel in) {
Tomasz Wasilczykeab3e552018-01-11 20:12:10 -08001611 mSelector = Objects.requireNonNull(in.readTypedObject(ProgramSelector.CREATOR));
1612 mLogicallyTunedTo = in.readTypedObject(ProgramSelector.Identifier.CREATOR);
1613 mPhysicallyTunedTo = in.readTypedObject(ProgramSelector.Identifier.CREATOR);
1614 mRelatedContent = in.createTypedArrayList(ProgramSelector.Identifier.CREATOR);
1615 mInfoFlags = in.readInt();
1616 mSignalQuality = in.readInt();
1617 mMetadata = in.readTypedObject(RadioMetadata.CREATOR);
Tomasz Wasilczyk436128f2018-01-08 16:46:09 -08001618 mVendorInfo = Utils.readStringMap(in);
Eric Laurent2035ac82015-03-05 15:18:44 -08001619 }
1620
1621 public static final Parcelable.Creator<ProgramInfo> CREATOR
1622 = new Parcelable.Creator<ProgramInfo>() {
1623 public ProgramInfo createFromParcel(Parcel in) {
1624 return new ProgramInfo(in);
1625 }
1626
1627 public ProgramInfo[] newArray(int size) {
1628 return new ProgramInfo[size];
1629 }
1630 };
1631
1632 @Override
1633 public void writeToParcel(Parcel dest, int flags) {
Tomasz Wasilczykeab3e552018-01-11 20:12:10 -08001634 dest.writeTypedObject(mSelector, flags);
1635 dest.writeTypedObject(mLogicallyTunedTo, flags);
1636 dest.writeTypedObject(mPhysicallyTunedTo, flags);
1637 Utils.writeTypedCollection(dest, mRelatedContent);
1638 dest.writeInt(mInfoFlags);
1639 dest.writeInt(mSignalQuality);
1640 dest.writeTypedObject(mMetadata, flags);
Tomasz Wasilczyk436128f2018-01-08 16:46:09 -08001641 Utils.writeStringMap(dest, mVendorInfo);
Eric Laurent2035ac82015-03-05 15:18:44 -08001642 }
1643
1644 @Override
1645 public int describeContents() {
1646 return 0;
1647 }
1648
1649 @Override
1650 public String toString() {
Tomasz Wasilczykeab3e552018-01-11 20:12:10 -08001651 return "ProgramInfo"
1652 + " [selector=" + mSelector
1653 + ", logicallyTunedTo=" + Objects.toString(mLogicallyTunedTo)
1654 + ", physicallyTunedTo=" + Objects.toString(mPhysicallyTunedTo)
1655 + ", relatedContent=" + mRelatedContent.size()
1656 + ", infoFlags=" + mInfoFlags
1657 + ", mSignalQuality=" + mSignalQuality
1658 + ", mMetadata=" + Objects.toString(mMetadata)
Eric Laurent2035ac82015-03-05 15:18:44 -08001659 + "]";
1660 }
1661
1662 @Override
1663 public int hashCode() {
Tomasz Wasilczykeab3e552018-01-11 20:12:10 -08001664 return Objects.hash(mSelector, mLogicallyTunedTo, mPhysicallyTunedTo,
1665 mRelatedContent, mInfoFlags, mSignalQuality, mMetadata, mVendorInfo);
Eric Laurent2035ac82015-03-05 15:18:44 -08001666 }
1667
1668 @Override
1669 public boolean equals(Object obj) {
Tomasz Wasilczykeab3e552018-01-11 20:12:10 -08001670 if (this == obj) return true;
1671 if (!(obj instanceof ProgramInfo)) return false;
Eric Laurent2035ac82015-03-05 15:18:44 -08001672 ProgramInfo other = (ProgramInfo) obj;
Tomasz Wasilczykeab3e552018-01-11 20:12:10 -08001673
1674 if (!Objects.equals(mSelector, other.mSelector)) return false;
1675 if (!Objects.equals(mLogicallyTunedTo, other.mLogicallyTunedTo)) return false;
1676 if (!Objects.equals(mPhysicallyTunedTo, other.mPhysicallyTunedTo)) return false;
1677 if (!Objects.equals(mRelatedContent, other.mRelatedContent)) return false;
1678 if (mInfoFlags != other.mInfoFlags) return false;
1679 if (mSignalQuality != other.mSignalQuality) return false;
1680 if (!Objects.equals(mMetadata, other.mMetadata)) return false;
1681 if (!Objects.equals(mVendorInfo, other.mVendorInfo)) return false;
1682
Eric Laurent2035ac82015-03-05 15:18:44 -08001683 return true;
1684 }
1685 }
1686
1687
1688 /**
1689 * Returns a list of descriptors for all broadcast radio modules present on the device.
1690 * @param modules An List of {@link ModuleProperties} where the list will be returned.
1691 * @return
1692 * <ul>
1693 * <li>{@link #STATUS_OK} in case of success, </li>
1694 * <li>{@link #STATUS_ERROR} in case of unspecified error, </li>
1695 * <li>{@link #STATUS_NO_INIT} if the native service cannot be reached, </li>
1696 * <li>{@link #STATUS_BAD_VALUE} if modules is null, </li>
1697 * <li>{@link #STATUS_DEAD_OBJECT} if the binder transaction to the native service fails, </li>
1698 * </ul>
1699 */
Tomasz Wasilczyk749e3dc2017-07-28 13:20:41 -07001700 @RequiresPermission(Manifest.permission.ACCESS_BROADCAST_RADIO)
Tomasz Wasilczykd15c9df2017-06-13 10:10:36 -07001701 public int listModules(List<ModuleProperties> modules) {
1702 if (modules == null) {
1703 Log.e(TAG, "the output list must not be empty");
1704 return STATUS_BAD_VALUE;
1705 }
1706
Tomasz Wasilczyk14752372017-06-21 11:58:21 -07001707 Log.d(TAG, "Listing available tuners...");
Tomasz Wasilczykd15c9df2017-06-13 10:10:36 -07001708 List<ModuleProperties> returnedList;
1709 try {
1710 returnedList = mService.listModules();
1711 } catch (RemoteException e) {
1712 Log.e(TAG, "Failed listing available tuners", e);
1713 return STATUS_DEAD_OBJECT;
1714 }
1715
1716 if (returnedList == null) {
1717 Log.e(TAG, "Returned list was a null");
1718 return STATUS_ERROR;
1719 }
1720
1721 modules.addAll(returnedList);
1722 return STATUS_OK;
1723 }
1724
1725 private native int nativeListModules(List<ModuleProperties> modules);
Eric Laurent2035ac82015-03-05 15:18:44 -08001726
1727 /**
1728 * Open an interface to control a tuner on a given broadcast radio module.
1729 * Optionally selects and applies the configuration passed as "config" argument.
1730 * @param moduleId radio module identifier {@link ModuleProperties#getId()}. Mandatory.
1731 * @param config desired band and configuration to apply when enabling the hardware module.
1732 * optional, can be null.
1733 * @param withAudio {@code true} to request a tuner with an audio source.
1734 * This tuner is intended for live listening or recording or a radio program.
1735 * If {@code false}, the tuner can only be used to retrieve program informations.
1736 * @param callback {@link RadioTuner.Callback} interface. Mandatory.
1737 * @param handler the Handler on which the callbacks will be received.
1738 * Can be null if default handler is OK.
1739 * @return a valid {@link RadioTuner} interface in case of success or null in case of error.
1740 */
Tomasz Wasilczyk749e3dc2017-07-28 13:20:41 -07001741 @RequiresPermission(Manifest.permission.ACCESS_BROADCAST_RADIO)
Eric Laurent2035ac82015-03-05 15:18:44 -08001742 public RadioTuner openTuner(int moduleId, BandConfig config, boolean withAudio,
1743 RadioTuner.Callback callback, Handler handler) {
1744 if (callback == null) {
Tomasz Wasilczyk347192e2017-04-04 11:13:44 -07001745 throw new IllegalArgumentException("callback must not be empty");
Eric Laurent2035ac82015-03-05 15:18:44 -08001746 }
Tomasz Wasilczyk347192e2017-04-04 11:13:44 -07001747
Tomasz Wasilczyk9b595f32017-06-21 11:14:19 -07001748 Log.d(TAG, "Opening tuner " + moduleId + "...");
Tomasz Wasilczyk21348172017-04-20 14:02:42 -07001749
Tomasz Wasilczyk9b595f32017-06-21 11:14:19 -07001750 ITuner tuner;
Tomasz Wasilczyk24250ef2017-07-13 15:59:08 -07001751 TunerCallbackAdapter halCallback = new TunerCallbackAdapter(callback, handler);
Tomasz Wasilczyk9b595f32017-06-21 11:14:19 -07001752 try {
1753 tuner = mService.openTuner(moduleId, config, withAudio, halCallback);
Tomasz Wasilczykca98cde2018-01-04 12:26:40 -08001754 } catch (RemoteException | IllegalArgumentException ex) {
1755 Log.e(TAG, "Failed to open tuner", ex);
Tomasz Wasilczyk9b595f32017-06-21 11:14:19 -07001756 return null;
Eric Laurent2035ac82015-03-05 15:18:44 -08001757 }
Tomasz Wasilczyk9b595f32017-06-21 11:14:19 -07001758 if (tuner == null) {
Tomasz Wasilczyk347192e2017-04-04 11:13:44 -07001759 Log.e(TAG, "Failed to open tuner");
Tomasz Wasilczyk9b595f32017-06-21 11:14:19 -07001760 return null;
Tomasz Wasilczyk347192e2017-04-04 11:13:44 -07001761 }
Tomasz Wasilczyk436128f2018-01-08 16:46:09 -08001762 return new TunerAdapter(tuner, halCallback,
1763 config != null ? config.getType() : BAND_INVALID);
Eric Laurent2035ac82015-03-05 15:18:44 -08001764 }
1765
Tomasz Wasilczykf151a7b2018-01-11 16:03:46 -08001766 private final Map<Announcement.OnListUpdatedListener, ICloseHandle> mAnnouncementListeners =
1767 new HashMap<>();
1768
1769 /**
1770 * Adds new announcement listener.
1771 *
1772 * @param enabledAnnouncementTypes a set of announcement types to listen to
1773 * @param listener announcement listener
1774 */
1775 @RequiresPermission(Manifest.permission.ACCESS_BROADCAST_RADIO)
1776 public void addAnnouncementListener(@NonNull Set<Integer> enabledAnnouncementTypes,
1777 @NonNull Announcement.OnListUpdatedListener listener) {
1778 addAnnouncementListener(cmd -> cmd.run(), enabledAnnouncementTypes, listener);
1779 }
1780
1781 /**
1782 * Adds new announcement listener with executor.
1783 *
1784 * @param executor the executor
1785 * @param enabledAnnouncementTypes a set of announcement types to listen to
1786 * @param listener announcement listener
1787 */
1788 @RequiresPermission(Manifest.permission.ACCESS_BROADCAST_RADIO)
1789 public void addAnnouncementListener(@NonNull @CallbackExecutor Executor executor,
1790 @NonNull Set<Integer> enabledAnnouncementTypes,
1791 @NonNull Announcement.OnListUpdatedListener listener) {
1792 Objects.requireNonNull(executor);
1793 Objects.requireNonNull(listener);
1794 int[] types = enabledAnnouncementTypes.stream().mapToInt(Integer::intValue).toArray();
1795 IAnnouncementListener listenerIface = new IAnnouncementListener.Stub() {
1796 public void onListUpdated(List<Announcement> activeAnnouncements) {
1797 executor.execute(() -> listener.onListUpdated(activeAnnouncements));
1798 }
1799 };
1800 synchronized (mAnnouncementListeners) {
1801 ICloseHandle closeHandle = null;
1802 try {
1803 closeHandle = mService.addAnnouncementListener(types, listenerIface);
1804 } catch (RemoteException ex) {
1805 ex.rethrowFromSystemServer();
1806 }
1807 Objects.requireNonNull(closeHandle);
1808 ICloseHandle oldCloseHandle = mAnnouncementListeners.put(listener, closeHandle);
1809 if (oldCloseHandle != null) Utils.close(oldCloseHandle);
1810 }
1811 }
1812
1813 /**
1814 * Removes previously registered announcement listener.
1815 *
1816 * @param listener announcement listener, previously registered with
1817 * {@link addAnnouncementListener}
1818 */
1819 @RequiresPermission(Manifest.permission.ACCESS_BROADCAST_RADIO)
1820 public void removeAnnouncementListener(@NonNull Announcement.OnListUpdatedListener listener) {
1821 Objects.requireNonNull(listener);
1822 synchronized (mAnnouncementListeners) {
1823 ICloseHandle closeHandle = mAnnouncementListeners.remove(listener);
1824 if (closeHandle != null) Utils.close(closeHandle);
1825 }
1826 }
1827
Tomasz Wasilczyk347192e2017-04-04 11:13:44 -07001828 @NonNull private final Context mContext;
Tomasz Wasilczyk9b595f32017-06-21 11:14:19 -07001829 @NonNull private final IRadioService mService;
Eric Laurent2035ac82015-03-05 15:18:44 -08001830
1831 /**
1832 * @hide
1833 */
Tomasz Wasilczyk347192e2017-04-04 11:13:44 -07001834 public RadioManager(@NonNull Context context) throws ServiceNotFoundException {
Eric Laurent2035ac82015-03-05 15:18:44 -08001835 mContext = context;
Tomasz Wasilczyk9b595f32017-06-21 11:14:19 -07001836 mService = IRadioService.Stub.asInterface(
1837 ServiceManager.getServiceOrThrow(Context.RADIO_SERVICE));
Eric Laurent2035ac82015-03-05 15:18:44 -08001838 }
1839}