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