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.os.Handler;
21 import android.os.Looper;
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 ITunerCallback interface by forwarding calls to RadioTuner.Callback.
32  */
33 final class TunerCallbackAdapter extends ITunerCallback.Stub {
34     private static final String TAG = "BroadcastRadio.TunerCallbackAdapter";
35 
36     private final Object mLock = new Object();
37     private final RadioTuner.Callback mCallback;
38     private final Handler mHandler;
39 
40     @GuardedBy("mLock")
41     @Nullable ProgramList mProgramList;
42 
43     // cache for deprecated methods
44     @GuardedBy("mLock")
45     boolean mIsAntennaConnected = true;
46 
47     @GuardedBy("mLock")
48     @Nullable List<RadioManager.ProgramInfo> mLastCompleteList;
49 
50     @GuardedBy("mLock")
51     private boolean mDelayedCompleteCallback;
52 
53     @GuardedBy("mLock")
54     @Nullable RadioManager.ProgramInfo mCurrentProgramInfo;
55 
TunerCallbackAdapter(RadioTuner.Callback callback, @Nullable Handler handler)56     TunerCallbackAdapter(RadioTuner.Callback callback, @Nullable Handler handler) {
57         mCallback = Objects.requireNonNull(callback, "Callback cannot be null");
58         if (handler == null) {
59             mHandler = new Handler(Looper.getMainLooper());
60         } else {
61             mHandler = handler;
62         }
63     }
64 
close()65     void close() {
66         synchronized (mLock) {
67             if (mProgramList != null) {
68                 mProgramList.close();
69             }
70         }
71     }
72 
setProgramListObserver(@ullable ProgramList programList, ProgramList.OnCloseListener closeListener)73     void setProgramListObserver(@Nullable ProgramList programList,
74             ProgramList.OnCloseListener closeListener) {
75         Objects.requireNonNull(closeListener, "CloseListener cannot be null");
76         synchronized (mLock) {
77             if (mProgramList != null) {
78                 Log.w(TAG, "Previous program list observer wasn't properly closed, closing it...");
79                 mProgramList.close();
80             }
81             mProgramList = programList;
82             if (programList == null) {
83                 return;
84             }
85             programList.setOnCloseListener(() -> {
86                 synchronized (mLock) {
87                     if (mProgramList != programList) {
88                         return;
89                     }
90                     mProgramList = null;
91                     mLastCompleteList = null;
92                 }
93                 closeListener.onClose();
94             });
95             programList.addOnCompleteListener(() -> {
96                 synchronized (mLock) {
97                     if (mProgramList != programList) {
98                         return;
99                     }
100                     mLastCompleteList = programList.toList();
101                     if (mDelayedCompleteCallback) {
102                         Log.d(TAG, "Sending delayed onBackgroundScanComplete callback");
103                         sendBackgroundScanCompleteLocked();
104                     }
105                 }
106             });
107         }
108     }
109 
getLastCompleteList()110     @Nullable List<RadioManager.ProgramInfo> getLastCompleteList() {
111         synchronized (mLock) {
112             return mLastCompleteList;
113         }
114     }
115 
clearLastCompleteList()116     void clearLastCompleteList() {
117         synchronized (mLock) {
118             mLastCompleteList = null;
119         }
120     }
121 
getCurrentProgramInformation()122     @Nullable RadioManager.ProgramInfo getCurrentProgramInformation() {
123         synchronized (mLock) {
124             return mCurrentProgramInfo;
125         }
126     }
127 
isAntennaConnected()128     boolean isAntennaConnected() {
129         boolean isConnected;
130         synchronized (mLock) {
131             isConnected = mIsAntennaConnected;
132         }
133         return isConnected;
134     }
135 
136     @Override
onError(int status)137     public void onError(int status) {
138         mHandler.post(() -> mCallback.onError(status));
139     }
140 
141     @Override
onTuneFailed(int status, @Nullable ProgramSelector selector)142     public void onTuneFailed(int status, @Nullable ProgramSelector selector) {
143         mHandler.post(() -> mCallback.onTuneFailed(status, selector));
144 
145         int errorCode;
146         switch (status) {
147             case RadioTuner.TUNER_RESULT_CANCELED:
148                 errorCode = RadioTuner.ERROR_CANCELLED;
149                 break;
150             case RadioManager.STATUS_PERMISSION_DENIED:
151             case RadioManager.STATUS_DEAD_OBJECT:
152                 errorCode = RadioTuner.ERROR_SERVER_DIED;
153                 break;
154             case RadioManager.STATUS_ERROR:
155             case RadioManager.STATUS_NO_INIT:
156             case RadioManager.STATUS_BAD_VALUE:
157             case RadioManager.STATUS_INVALID_OPERATION:
158             case RadioTuner.TUNER_RESULT_INTERNAL_ERROR:
159             case RadioTuner.TUNER_RESULT_INVALID_ARGUMENTS:
160             case RadioTuner.TUNER_RESULT_INVALID_STATE:
161             case RadioTuner.TUNER_RESULT_NOT_SUPPORTED:
162             case RadioTuner.TUNER_RESULT_UNKNOWN_ERROR:
163                 Log.i(TAG, "Got an error with no mapping to the legacy API (" + status
164                         + "), doing a best-effort conversion to ERROR_SCAN_TIMEOUT");
165             // fall through
166             case RadioManager.STATUS_TIMED_OUT:
167             case RadioTuner.TUNER_RESULT_TIMEOUT:
168             default:
169                 errorCode = RadioTuner.ERROR_SCAN_TIMEOUT;
170         }
171         mHandler.post(() -> mCallback.onError(errorCode));
172     }
173 
174     @Override
onConfigurationChanged(RadioManager.BandConfig config)175     public void onConfigurationChanged(RadioManager.BandConfig config) {
176         mHandler.post(() -> mCallback.onConfigurationChanged(config));
177     }
178 
179     @Override
onCurrentProgramInfoChanged(RadioManager.ProgramInfo info)180     public void onCurrentProgramInfoChanged(RadioManager.ProgramInfo info) {
181         if (info == null) {
182             Log.e(TAG, "ProgramInfo must not be null");
183             return;
184         }
185 
186         synchronized (mLock) {
187             mCurrentProgramInfo = info;
188         }
189 
190         mHandler.post(() -> {
191             mCallback.onProgramInfoChanged(info);
192 
193             RadioMetadata metadata = info.getMetadata();
194             if (metadata != null) mCallback.onMetadataChanged(metadata);
195         });
196     }
197 
198     @Override
onTrafficAnnouncement(boolean active)199     public void onTrafficAnnouncement(boolean active) {
200         mHandler.post(() -> mCallback.onTrafficAnnouncement(active));
201     }
202 
203     @Override
onEmergencyAnnouncement(boolean active)204     public void onEmergencyAnnouncement(boolean active) {
205         mHandler.post(() -> mCallback.onEmergencyAnnouncement(active));
206     }
207 
208     @Override
onAntennaState(boolean connected)209     public void onAntennaState(boolean connected) {
210         synchronized (mLock) {
211             mIsAntennaConnected = connected;
212         }
213         mHandler.post(() -> mCallback.onAntennaState(connected));
214     }
215 
216     @Override
onBackgroundScanAvailabilityChange(boolean isAvailable)217     public void onBackgroundScanAvailabilityChange(boolean isAvailable) {
218         mHandler.post(() -> mCallback.onBackgroundScanAvailabilityChange(isAvailable));
219     }
220 
221     @GuardedBy("mLock")
sendBackgroundScanCompleteLocked()222     private void sendBackgroundScanCompleteLocked() {
223         mDelayedCompleteCallback = false;
224         mHandler.post(() -> mCallback.onBackgroundScanComplete());
225     }
226 
227     @Override
onBackgroundScanComplete()228     public void onBackgroundScanComplete() {
229         synchronized (mLock) {
230             if (mLastCompleteList == null) {
231                 Log.i(TAG, "Got onBackgroundScanComplete callback, but the "
232                         + "program list didn't get through yet. Delaying it...");
233                 mDelayedCompleteCallback = true;
234                 return;
235             }
236             sendBackgroundScanCompleteLocked();
237         }
238     }
239 
240     @Override
onProgramListChanged()241     public void onProgramListChanged() {
242         mHandler.post(() -> mCallback.onProgramListChanged());
243     }
244 
245     @Override
onProgramListUpdated(ProgramList.Chunk chunk)246     public void onProgramListUpdated(ProgramList.Chunk chunk) {
247         mHandler.post(() -> {
248             synchronized (mLock) {
249                 if (mProgramList == null) {
250                     return;
251                 }
252                 mProgramList.apply(Objects.requireNonNull(chunk, "Chunk cannot be null"));
253             }
254         });
255     }
256 
257     @Override
onConfigFlagUpdated(@adioManager.ConfigFlag int flag, boolean value)258     public void onConfigFlagUpdated(@RadioManager.ConfigFlag int flag, boolean value) {
259         mHandler.post(() -> mCallback.onConfigFlagUpdated(flag, value));
260     }
261 
262     @Override
onParametersUpdated(Map<String, String> parameters)263     public void onParametersUpdated(Map<String, String> parameters) {
264         mHandler.post(() -> mCallback.onParametersUpdated(parameters));
265     }
266 }
267