• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 com.android.car.radio.platform;
18 
19 import android.hardware.radio.ProgramSelector;
20 import android.hardware.radio.RadioManager;
21 import android.hardware.radio.RadioMetadata;
22 import android.hardware.radio.RadioTuner;
23 import android.os.Handler;
24 import android.os.Looper;
25 
26 import androidx.annotation.Nullable;
27 
28 import com.android.car.radio.util.Log;
29 import com.android.internal.annotations.GuardedBy;
30 
31 import java.util.Map;
32 import java.util.Objects;
33 import java.util.concurrent.atomic.AtomicReference;
34 
35 /**
36  * Proposed extensions to android.hardware.radio.TunerCallbackAdapter.
37  *
38  * This class is not compatible with the original at all (because they operate
39  * on different callback types), it just represents a proposed feature
40  * extensions.
41  *
42  * They might eventually get pushed to the framework.
43  */
44 final class TunerCallbackAdapterExt extends RadioTuner.Callback {
45     private static final String TAG = "BcRadioApp.tunerext";
46     private static final int INIT_TIMEOUT_MS = 10000;  // 10s
47 
48     private final Object mInitLock = new Object();
49     private boolean mIsInitialized = false;
50 
51     private final RadioTuner.Callback mCallback;
52     private final Handler mHandler;
53 
54     private final AtomicReference<TuneFailedCallback> mTuneFailedCallback = new AtomicReference<>();
55     private final Object mProgramInfoLock = new Object();
56     @GuardedBy("mProgramInfoLock")
57     private ProgramInfoCallback mProgramInfoCallback;
58     @GuardedBy("mProgramInfoLock")
59     private RadioManager.ProgramInfo mCachedProgramInfo;
60 
61     interface TuneFailedCallback {
onTuneFailed(int result, @Nullable ProgramSelector selector)62         void onTuneFailed(int result, @Nullable ProgramSelector selector);
63     }
64 
65     interface ProgramInfoCallback {
onProgramInfoChanged(RadioManager.ProgramInfo info)66         void onProgramInfoChanged(RadioManager.ProgramInfo info);
67     }
68 
TunerCallbackAdapterExt(RadioTuner.Callback callback, @Nullable Handler handler)69     TunerCallbackAdapterExt(RadioTuner.Callback callback, @Nullable Handler handler) {
70         mCallback = Objects.requireNonNull(callback, "Callback cannot be null");
71         if (handler == null) {
72             mHandler = new Handler(Looper.getMainLooper());
73         } else {
74             mHandler = handler;
75         }
76     }
77 
waitForInitialization()78     public boolean waitForInitialization() {
79         synchronized (mInitLock) {
80             if (mIsInitialized) return true;
81             try {
82                 mInitLock.wait(INIT_TIMEOUT_MS);
83             } catch (InterruptedException ex) {
84                 // ignore the exception, as we check mIsInitialized anyway
85             }
86             return mIsInitialized;
87         }
88     }
89 
setTuneFailedCallback(TuneFailedCallback cb)90     void setTuneFailedCallback(TuneFailedCallback cb) {
91         mTuneFailedCallback.set(cb);
92     }
93 
setProgramInfoCallback(ProgramInfoCallback cb)94     void setProgramInfoCallback(ProgramInfoCallback cb) {
95         synchronized (mProgramInfoLock) {
96             mProgramInfoCallback = cb;
97             if (mProgramInfoCallback != null && mCachedProgramInfo != null) {
98                 Log.d(TAG, "Invoking callback with cached ProgramInfo");
99                 mProgramInfoCallback.onProgramInfoChanged(mCachedProgramInfo);
100                 mCachedProgramInfo = null;
101             }
102         }
103     }
104 
105     @Override
onError(int status)106     public void onError(int status) {
107         mHandler.post(() -> mCallback.onError(status));
108     }
109 
110     @Override
onTuneFailed(int result, @Nullable ProgramSelector selector)111     public void onTuneFailed(int result, @Nullable ProgramSelector selector) {
112         TuneFailedCallback cb = mTuneFailedCallback.get();
113         if (cb != null) {
114             cb.onTuneFailed(result, selector);
115         }
116         mHandler.post(() -> mCallback.onTuneFailed(result, selector));
117     }
118 
119     @Override
onConfigurationChanged(RadioManager.BandConfig config)120     public void onConfigurationChanged(RadioManager.BandConfig config) {
121         mHandler.post(() -> mCallback.onConfigurationChanged(config));
122         if (mIsInitialized) return;
123         synchronized (mInitLock) {
124             mIsInitialized = true;
125             mInitLock.notifyAll();
126         }
127     }
128 
onProgramInfoChanged(RadioManager.ProgramInfo info)129     public void onProgramInfoChanged(RadioManager.ProgramInfo info) {
130         synchronized (mProgramInfoLock) {
131             if (mProgramInfoCallback == null) {
132                 // Cache the ProgramInfo until the callback is set. This workaround is needed
133                 // because a TunerCallbackAdapterExt needed to call RadioManager.openTuner(), but
134                 // the return of that function is needed to create a RadioManagerExt, which calls
135                 // sets the callback through setProgramInfoCallback().
136                 Log.d(TAG, "ProgramInfo callback is not set yet; caching ProgramInfo");
137                 mCachedProgramInfo = info;
138             } else {
139                 mProgramInfoCallback.onProgramInfoChanged(info);
140             }
141         }
142         mHandler.post(() -> mCallback.onProgramInfoChanged(info));
143     }
144 
onMetadataChanged(RadioMetadata metadata)145     public void onMetadataChanged(RadioMetadata metadata) {
146         mHandler.post(() -> mCallback.onMetadataChanged(metadata));
147     }
148 
onTrafficAnnouncement(boolean active)149     public void onTrafficAnnouncement(boolean active) {
150         mHandler.post(() -> mCallback.onTrafficAnnouncement(active));
151     }
152 
onEmergencyAnnouncement(boolean active)153     public void onEmergencyAnnouncement(boolean active) {
154         mHandler.post(() -> mCallback.onEmergencyAnnouncement(active));
155     }
156 
onAntennaState(boolean connected)157     public void onAntennaState(boolean connected) {
158         mHandler.post(() -> mCallback.onAntennaState(connected));
159     }
160 
onControlChanged(boolean control)161     public void onControlChanged(boolean control) {
162         mHandler.post(() -> mCallback.onControlChanged(control));
163     }
164 
onBackgroundScanAvailabilityChange(boolean isAvailable)165     public void onBackgroundScanAvailabilityChange(boolean isAvailable) {
166         mHandler.post(() -> mCallback.onBackgroundScanAvailabilityChange(isAvailable));
167     }
168 
onBackgroundScanComplete()169     public void onBackgroundScanComplete() {
170         mHandler.post(() -> mCallback.onBackgroundScanComplete());
171     }
172 
onProgramListChanged()173     public void onProgramListChanged() {
174         mHandler.post(() -> mCallback.onProgramListChanged());
175     }
176 
onParametersUpdated(Map<String, String> parameters)177     public void onParametersUpdated(Map<String, String> parameters) {
178         mHandler.post(() -> mCallback.onParametersUpdated(parameters));
179     }
180 }
181