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