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