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.server.broadcastradio.hal1;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.hardware.radio.ITunerCallback;
22 import android.hardware.radio.ProgramList;
23 import android.hardware.radio.ProgramSelector;
24 import android.hardware.radio.RadioManager;
25 import android.hardware.radio.RadioTuner;
26 import android.os.IBinder;
27 import android.os.RemoteException;
28 
29 import com.android.server.utils.Slogf;
30 
31 import java.util.List;
32 import java.util.Map;
33 import java.util.Set;
34 import java.util.concurrent.atomic.AtomicReference;
35 import java.util.stream.Collectors;
36 
37 class TunerCallback implements ITunerCallback {
38 
39     private static final String TAG = "BcRadio1Srv.TunerCallback";
40 
41     /**
42      * This field is used by native code, do not access or modify.
43      */
44     private final long mNativeContext;
45 
46     private final Tuner mTuner;
47     private final ITunerCallback mClientCallback;
48 
49     private final AtomicReference<ProgramList.Filter> mProgramListFilter = new AtomicReference<>();
50     private boolean mInitialConfigurationDone = false;
51 
TunerCallback(@onNull Tuner tuner, @NonNull ITunerCallback clientCallback, int halRev)52     TunerCallback(@NonNull Tuner tuner, @NonNull ITunerCallback clientCallback, int halRev) {
53         mTuner = tuner;
54         mClientCallback = clientCallback;
55         mNativeContext = nativeInit(tuner, halRev);
56     }
57 
58     @Override
finalize()59     protected void finalize() throws Throwable {
60         nativeFinalize(mNativeContext);
61         super.finalize();
62     }
63 
nativeInit(@onNull Tuner tuner, int halRev)64     private native long nativeInit(@NonNull Tuner tuner, int halRev);
nativeFinalize(long nativeContext)65     private native void nativeFinalize(long nativeContext);
nativeDetach(long nativeContext)66     private native void nativeDetach(long nativeContext);
67 
detach()68     public void detach() {
69         nativeDetach(mNativeContext);
70     }
71 
72     private interface RunnableThrowingRemoteException {
run()73         void run() throws RemoteException;
74     }
75 
dispatch(RunnableThrowingRemoteException func)76     private void dispatch(RunnableThrowingRemoteException func) {
77         try {
78             func.run();
79         } catch (RemoteException e) {
80             Slogf.e(TAG, "client died", e);
81         }
82     }
83 
84     // called from native side
handleHwFailure()85     private void handleHwFailure() {
86         onError(RadioTuner.ERROR_HARDWARE_FAILURE);
87         mTuner.close();
88     }
89 
startProgramListUpdates(@ullable ProgramList.Filter filter)90     void startProgramListUpdates(@Nullable ProgramList.Filter filter) {
91         if (filter == null) filter = new ProgramList.Filter();
92         mProgramListFilter.set(filter);
93         sendProgramListUpdate();
94     }
95 
stopProgramListUpdates()96     void stopProgramListUpdates() {
97         mProgramListFilter.set(null);
98     }
99 
isInitialConfigurationDone()100     boolean isInitialConfigurationDone() {
101         return mInitialConfigurationDone;
102     }
103 
104     @Override
onError(int status)105     public void onError(int status) {
106         dispatch(() -> mClientCallback.onError(status));
107     }
108 
109     @Override
onTuneFailed(int result, ProgramSelector selector)110     public void onTuneFailed(int result, ProgramSelector selector) {
111         Slogf.e(TAG, "Not applicable for HAL 1.x");
112     }
113 
114     @Override
onConfigurationChanged(RadioManager.BandConfig config)115     public void onConfigurationChanged(RadioManager.BandConfig config) {
116         mInitialConfigurationDone = true;
117         dispatch(() -> mClientCallback.onConfigurationChanged(config));
118     }
119 
120     @Override
onCurrentProgramInfoChanged(RadioManager.ProgramInfo info)121     public void onCurrentProgramInfoChanged(RadioManager.ProgramInfo info) {
122         dispatch(() -> mClientCallback.onCurrentProgramInfoChanged(info));
123     }
124 
125     @Override
onTrafficAnnouncement(boolean active)126     public void onTrafficAnnouncement(boolean active) {
127         dispatch(() -> mClientCallback.onTrafficAnnouncement(active));
128     }
129 
130     @Override
onEmergencyAnnouncement(boolean active)131     public void onEmergencyAnnouncement(boolean active) {
132         dispatch(() -> mClientCallback.onEmergencyAnnouncement(active));
133     }
134 
135     @Override
onAntennaState(boolean connected)136     public void onAntennaState(boolean connected) {
137         dispatch(() -> mClientCallback.onAntennaState(connected));
138     }
139 
140     @Override
onBackgroundScanAvailabilityChange(boolean isAvailable)141     public void onBackgroundScanAvailabilityChange(boolean isAvailable) {
142         dispatch(() -> mClientCallback.onBackgroundScanAvailabilityChange(isAvailable));
143     }
144 
145     @Override
onBackgroundScanComplete()146     public void onBackgroundScanComplete() {
147         dispatch(() -> mClientCallback.onBackgroundScanComplete());
148     }
149 
150     @Override
onProgramListChanged()151     public void onProgramListChanged() {
152         dispatch(() -> mClientCallback.onProgramListChanged());
153         sendProgramListUpdate();
154     }
155 
sendProgramListUpdate()156     private void sendProgramListUpdate() {
157         ProgramList.Filter filter = mProgramListFilter.get();
158         if (filter == null) return;
159 
160         List<RadioManager.ProgramInfo> modified;
161         try {
162             modified = mTuner.getProgramList(filter.getVendorFilter());
163         } catch (IllegalStateException ex) {
164             Slogf.d(TAG, "Program list not ready yet");
165             return;
166         }
167         Set<RadioManager.ProgramInfo> modifiedSet = modified.stream().collect(Collectors.toSet());
168         ProgramList.Chunk chunk = new ProgramList.Chunk(true, true, modifiedSet, null);
169         dispatch(() -> mClientCallback.onProgramListUpdated(chunk));
170     }
171 
172     @Override
onProgramListUpdated(ProgramList.Chunk chunk)173     public void onProgramListUpdated(ProgramList.Chunk chunk) {
174         dispatch(() -> mClientCallback.onProgramListUpdated(chunk));
175     }
176 
177     @Override
onConfigFlagUpdated(int flag, boolean value)178     public void onConfigFlagUpdated(int flag, boolean value) {
179         Slogf.w(TAG, "Not applicable for HAL 1.x");
180     }
181 
182     @Override
onParametersUpdated(Map<String, String> parameters)183     public void onParametersUpdated(Map<String, String> parameters) {
184         Slogf.w(TAG, "Not applicable for HAL 1.x");
185     }
186 
187     @Override
asBinder()188     public IBinder asBinder() {
189         throw new RuntimeException("Not a binder");
190     }
191 }
192