1 /** 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.hardware.radio; 18 19 import android.annotation.Nullable; 20 import android.graphics.Bitmap; 21 import android.os.RemoteException; 22 import android.util.Log; 23 24 import com.android.internal.annotations.GuardedBy; 25 26 import java.util.List; 27 import java.util.Map; 28 import java.util.Objects; 29 30 /** 31 * Implements the RadioTuner interface by forwarding calls to radio service. 32 */ 33 final class TunerAdapter extends RadioTuner { 34 private static final String TAG = "BroadcastRadio.TunerAdapter"; 35 36 private final ITuner mTuner; 37 private final TunerCallbackAdapter mCallback; 38 private final Object mLock = new Object(); 39 40 @GuardedBy("mLock") 41 private boolean mIsClosed; 42 43 @GuardedBy("mLock") 44 @RadioManager.Band 45 private int mBand; 46 47 @GuardedBy("mLock") 48 private ProgramList mLegacyListProxy; 49 50 @GuardedBy("mLock") 51 private Map<String, String> mLegacyListFilter; 52 TunerAdapter(ITuner tuner, TunerCallbackAdapter callback, @RadioManager.Band int band)53 TunerAdapter(ITuner tuner, TunerCallbackAdapter callback, 54 @RadioManager.Band int band) { 55 mTuner = Objects.requireNonNull(tuner, "Tuner cannot be null"); 56 mCallback = Objects.requireNonNull(callback, "Callback cannot be null"); 57 mBand = band; 58 } 59 60 @Override close()61 public void close() { 62 synchronized (mLock) { 63 if (mIsClosed) { 64 Log.v(TAG, "Tuner is already closed"); 65 return; 66 } 67 mIsClosed = true; 68 if (mLegacyListProxy != null) { 69 mLegacyListProxy.close(); 70 mLegacyListProxy = null; 71 } 72 } 73 mCallback.close(); 74 try { 75 mTuner.close(); 76 } catch (RemoteException e) { 77 Log.e(TAG, "Exception trying to close tuner", e); 78 } 79 } 80 81 @Override setConfiguration(RadioManager.BandConfig config)82 public int setConfiguration(RadioManager.BandConfig config) { 83 if (config == null) { 84 return RadioManager.STATUS_BAD_VALUE; 85 } 86 try { 87 mTuner.setConfiguration(config); 88 synchronized (mLock) { 89 mBand = config.getType(); 90 } 91 return RadioManager.STATUS_OK; 92 } catch (IllegalArgumentException e) { 93 Log.e(TAG, "Can't set configuration", e); 94 return RadioManager.STATUS_BAD_VALUE; 95 } catch (RemoteException e) { 96 Log.e(TAG, "Service died", e); 97 return RadioManager.STATUS_DEAD_OBJECT; 98 } 99 } 100 101 @Override getConfiguration(RadioManager.BandConfig[] config)102 public int getConfiguration(RadioManager.BandConfig[] config) { 103 if (config == null || config.length != 1) { 104 throw new IllegalArgumentException("The argument must be an array of length 1"); 105 } 106 try { 107 config[0] = mTuner.getConfiguration(); 108 return RadioManager.STATUS_OK; 109 } catch (RemoteException e) { 110 Log.e(TAG, "Service died", e); 111 return RadioManager.STATUS_DEAD_OBJECT; 112 } 113 } 114 115 @Override setMute(boolean mute)116 public int setMute(boolean mute) { 117 try { 118 mTuner.setMuted(mute); 119 } catch (IllegalStateException e) { 120 Log.e(TAG, "Can't set muted", e); 121 return RadioManager.STATUS_ERROR; 122 } catch (RemoteException e) { 123 Log.e(TAG, "Service died", e); 124 return RadioManager.STATUS_DEAD_OBJECT; 125 } 126 return RadioManager.STATUS_OK; 127 } 128 129 @Override getMute()130 public boolean getMute() { 131 try { 132 return mTuner.isMuted(); 133 } catch (RemoteException e) { 134 Log.e(TAG, "Service died", e); 135 return true; 136 } 137 } 138 139 @Override step(int direction, boolean skipSubChannel)140 public int step(int direction, boolean skipSubChannel) { 141 try { 142 mTuner.step(/* directionDown= */ direction == RadioTuner.DIRECTION_DOWN, 143 skipSubChannel); 144 } catch (IllegalStateException e) { 145 Log.e(TAG, "Can't step", e); 146 return RadioManager.STATUS_INVALID_OPERATION; 147 } catch (RemoteException e) { 148 Log.e(TAG, "Service died", e); 149 return RadioManager.STATUS_DEAD_OBJECT; 150 } 151 return RadioManager.STATUS_OK; 152 } 153 154 @Override scan(int direction, boolean skipSubChannel)155 public int scan(int direction, boolean skipSubChannel) { 156 try { 157 mTuner.seek(/* directionDown= */ direction == RadioTuner.DIRECTION_DOWN, 158 skipSubChannel); 159 } catch (IllegalStateException e) { 160 Log.e(TAG, "Can't scan", e); 161 return RadioManager.STATUS_INVALID_OPERATION; 162 } catch (RemoteException e) { 163 Log.e(TAG, "Service died", e); 164 return RadioManager.STATUS_DEAD_OBJECT; 165 } 166 return RadioManager.STATUS_OK; 167 } 168 169 @Override seek(int direction, boolean skipSubChannel)170 public int seek(int direction, boolean skipSubChannel) { 171 try { 172 mTuner.seek(/* directionDown= */ direction == RadioTuner.DIRECTION_DOWN, 173 skipSubChannel); 174 } catch (IllegalStateException e) { 175 Log.e(TAG, "Can't seek", e); 176 return RadioManager.STATUS_INVALID_OPERATION; 177 } catch (RemoteException e) { 178 Log.e(TAG, "Service died", e); 179 return RadioManager.STATUS_DEAD_OBJECT; 180 } 181 return RadioManager.STATUS_OK; 182 } 183 184 @Override tune(int channel, int subChannel)185 public int tune(int channel, int subChannel) { 186 try { 187 int band; 188 synchronized (mLock) { 189 band = mBand; 190 } 191 mTuner.tune(ProgramSelector.createAmFmSelector(band, channel, subChannel)); 192 } catch (IllegalStateException e) { 193 Log.e(TAG, "Can't tune", e); 194 return RadioManager.STATUS_INVALID_OPERATION; 195 } catch (IllegalArgumentException e) { 196 Log.e(TAG, "Can't tune", e); 197 return RadioManager.STATUS_BAD_VALUE; 198 } catch (RemoteException e) { 199 Log.e(TAG, "Service died", e); 200 return RadioManager.STATUS_DEAD_OBJECT; 201 } 202 return RadioManager.STATUS_OK; 203 } 204 205 @Override tune(ProgramSelector selector)206 public void tune(ProgramSelector selector) { 207 try { 208 mTuner.tune(selector); 209 } catch (RemoteException e) { 210 throw new RuntimeException("Service died", e); 211 } 212 } 213 214 @Override cancel()215 public int cancel() { 216 try { 217 mTuner.cancel(); 218 } catch (IllegalStateException e) { 219 Log.e(TAG, "Can't cancel", e); 220 return RadioManager.STATUS_INVALID_OPERATION; 221 } catch (RemoteException e) { 222 Log.e(TAG, "Service died", e); 223 return RadioManager.STATUS_DEAD_OBJECT; 224 } 225 return RadioManager.STATUS_OK; 226 } 227 228 @Override cancelAnnouncement()229 public void cancelAnnouncement() { 230 try { 231 mTuner.cancelAnnouncement(); 232 } catch (RemoteException e) { 233 throw new RuntimeException("Service died", e); 234 } 235 } 236 237 @Override getProgramInformation(RadioManager.ProgramInfo[] info)238 public int getProgramInformation(RadioManager.ProgramInfo[] info) { 239 if (info == null || info.length != 1) { 240 Log.e(TAG, "The argument must be an array of length 1"); 241 return RadioManager.STATUS_BAD_VALUE; 242 } 243 244 RadioManager.ProgramInfo current = mCallback.getCurrentProgramInformation(); 245 if (current == null) { 246 Log.w(TAG, "Didn't get program info yet"); 247 return RadioManager.STATUS_INVALID_OPERATION; 248 } 249 info[0] = current; 250 return RadioManager.STATUS_OK; 251 } 252 253 @Override 254 @Nullable getMetadataImage(int id)255 public Bitmap getMetadataImage(int id) { 256 try { 257 return mTuner.getImage(id); 258 } catch (RemoteException e) { 259 throw new RuntimeException("Service died", e); 260 } 261 } 262 263 @Override startBackgroundScan()264 public boolean startBackgroundScan() { 265 try { 266 return mTuner.startBackgroundScan(); 267 } catch (RemoteException e) { 268 throw new RuntimeException("Service died", e); 269 } 270 } 271 272 @Override 273 public List<RadioManager.ProgramInfo> getProgramList(@ullable Map<String, String> vendorFilter)274 getProgramList(@Nullable Map<String, String> vendorFilter) { 275 synchronized (mLock) { 276 if (mLegacyListProxy == null || !Objects.equals(mLegacyListFilter, vendorFilter)) { 277 Log.i(TAG, "Program list filter has changed, requesting new list"); 278 mLegacyListProxy = new ProgramList(); 279 mLegacyListFilter = vendorFilter; 280 mCallback.clearLastCompleteList(); 281 mCallback.setProgramListObserver(mLegacyListProxy, () -> { 282 Log.i(TAG, "Empty closeListener in programListObserver"); 283 }); 284 } 285 } 286 try { 287 mTuner.startProgramListUpdates(new ProgramList.Filter(vendorFilter)); 288 } catch (RemoteException ex) { 289 throw new RuntimeException("Service died", ex); 290 } 291 292 List<RadioManager.ProgramInfo> list = mCallback.getLastCompleteList(); 293 if (list == null) { 294 throw new IllegalStateException("Program list is not ready yet"); 295 } 296 return list; 297 } 298 299 @Override 300 @Nullable getDynamicProgramList(@ullable ProgramList.Filter filter)301 public ProgramList getDynamicProgramList(@Nullable ProgramList.Filter filter) { 302 synchronized (mLock) { 303 if (mLegacyListProxy != null) { 304 mLegacyListProxy.close(); 305 mLegacyListProxy = null; 306 } 307 mLegacyListFilter = null; 308 } 309 ProgramList list = new ProgramList(); 310 mCallback.setProgramListObserver(list, () -> { 311 try { 312 mTuner.stopProgramListUpdates(); 313 } catch (IllegalStateException ex) { 314 // it's fine to not stop updates if tuner is already closed 315 Log.e(TAG, "Tuner may already be closed", ex); 316 } catch (RemoteException ex) { 317 Log.e(TAG, "Couldn't stop program list updates", ex); 318 } 319 }); 320 321 try { 322 mTuner.startProgramListUpdates(filter); 323 } catch (UnsupportedOperationException ex) { 324 Log.i(TAG, "Program list is not supported with this hardware"); 325 return null; 326 } catch (RemoteException ex) { 327 mCallback.setProgramListObserver(null, () -> { 328 Log.i(TAG, "Empty closeListener in programListObserver"); 329 }); 330 throw new RuntimeException("Service died", ex); 331 } 332 333 return list; 334 } 335 336 @Override isAnalogForced()337 public boolean isAnalogForced() { 338 try { 339 return isConfigFlagSet(RadioManager.CONFIG_FORCE_ANALOG); 340 } catch (UnsupportedOperationException ex) { 341 throw new IllegalStateException(ex); 342 } 343 } 344 345 @Override setAnalogForced(boolean isForced)346 public void setAnalogForced(boolean isForced) { 347 try { 348 setConfigFlag(RadioManager.CONFIG_FORCE_ANALOG, isForced); 349 } catch (UnsupportedOperationException ex) { 350 throw new IllegalStateException(ex); 351 } 352 } 353 354 @Override isConfigFlagSupported(@adioManager.ConfigFlag int flag)355 public boolean isConfigFlagSupported(@RadioManager.ConfigFlag int flag) { 356 try { 357 return mTuner.isConfigFlagSupported(flag); 358 } catch (RemoteException e) { 359 throw new RuntimeException("Service died", e); 360 } 361 } 362 363 @Override isConfigFlagSet(@adioManager.ConfigFlag int flag)364 public boolean isConfigFlagSet(@RadioManager.ConfigFlag int flag) { 365 try { 366 return mTuner.isConfigFlagSet(flag); 367 } catch (RemoteException e) { 368 throw new RuntimeException("Service died", e); 369 } 370 } 371 372 @Override setConfigFlag(@adioManager.ConfigFlag int flag, boolean value)373 public void setConfigFlag(@RadioManager.ConfigFlag int flag, boolean value) { 374 try { 375 mTuner.setConfigFlag(flag, value); 376 } catch (RemoteException e) { 377 throw new RuntimeException("Service died", e); 378 } 379 } 380 381 @Override setParameters(Map<String, String> parameters)382 public Map<String, String> setParameters(Map<String, String> parameters) { 383 try { 384 return mTuner.setParameters(Objects.requireNonNull(parameters, 385 "Parameters cannot be null")); 386 } catch (RemoteException e) { 387 throw new RuntimeException("Service died", e); 388 } 389 } 390 391 @Override getParameters(List<String> keys)392 public Map<String, String> getParameters(List<String> keys) { 393 try { 394 return mTuner.getParameters(Objects.requireNonNull(keys, "Keys cannot be null")); 395 } catch (RemoteException e) { 396 throw new RuntimeException("Service died", e); 397 } 398 } 399 400 @Override isAntennaConnected()401 public boolean isAntennaConnected() { 402 return mCallback.isAntennaConnected(); 403 } 404 405 @Override hasControl()406 public boolean hasControl() { 407 try { 408 // don't rely on mIsClosed, as tuner might get closed internally 409 return !mTuner.isClosed(); 410 } catch (RemoteException e) { 411 return false; 412 } 413 } 414 } 415