• 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.server.broadcastradio.hal1;
18 
19 import android.annotation.NonNull;
20 import android.graphics.Bitmap;
21 import android.graphics.BitmapFactory;
22 import android.hardware.radio.ITuner;
23 import android.hardware.radio.ITunerCallback;
24 import android.hardware.radio.ProgramList;
25 import android.hardware.radio.ProgramSelector;
26 import android.hardware.radio.RadioManager;
27 import android.os.IBinder;
28 import android.os.RemoteException;
29 import android.util.Slog;
30 
31 import java.util.List;
32 import java.util.Map;
33 
34 class Tuner extends ITuner.Stub {
35     private static final String TAG = "BroadcastRadioService.Tuner";
36 
37     /**
38      * This field is used by native code, do not access or modify.
39      */
40     private final long mNativeContext;
41 
42     private final Object mLock = new Object();
43     @NonNull private final TunerCallback mTunerCallback;
44     @NonNull private final ITunerCallback mClientCallback;
45     @NonNull private final IBinder.DeathRecipient mDeathRecipient;
46 
47     private boolean mIsClosed = false;
48     private boolean mIsMuted = false;
49     private int mRegion;
50     private final boolean mWithAudio;
51 
Tuner(@onNull ITunerCallback clientCallback, int halRev, int region, boolean withAudio, int band)52     Tuner(@NonNull ITunerCallback clientCallback, int halRev,
53             int region, boolean withAudio, int band) {
54         mClientCallback = clientCallback;
55         mTunerCallback = new TunerCallback(this, clientCallback, halRev);
56         mRegion = region;
57         mWithAudio = withAudio;
58         mNativeContext = nativeInit(halRev, withAudio, band);
59         mDeathRecipient = this::close;
60         try {
61             mClientCallback.asBinder().linkToDeath(mDeathRecipient, 0);
62         } catch (RemoteException ex) {
63             close();
64         }
65     }
66 
67     @Override
finalize()68     protected void finalize() throws Throwable {
69         nativeFinalize(mNativeContext);
70         super.finalize();
71     }
72 
nativeInit(int halRev, boolean withAudio, int band)73     private native long nativeInit(int halRev, boolean withAudio, int band);
nativeFinalize(long nativeContext)74     private native void nativeFinalize(long nativeContext);
nativeClose(long nativeContext)75     private native void nativeClose(long nativeContext);
76 
nativeSetConfiguration(long nativeContext, @NonNull RadioManager.BandConfig config)77     private native void nativeSetConfiguration(long nativeContext,
78             @NonNull RadioManager.BandConfig config);
nativeGetConfiguration(long nativeContext, int region)79     private native RadioManager.BandConfig nativeGetConfiguration(long nativeContext, int region);
80 
nativeStep(long nativeContext, boolean directionDown, boolean skipSubChannel)81     private native void nativeStep(long nativeContext, boolean directionDown, boolean skipSubChannel);
nativeScan(long nativeContext, boolean directionDown, boolean skipSubChannel)82     private native void nativeScan(long nativeContext, boolean directionDown, boolean skipSubChannel);
nativeTune(long nativeContext, @NonNull ProgramSelector selector)83     private native void nativeTune(long nativeContext, @NonNull ProgramSelector selector);
nativeCancel(long nativeContext)84     private native void nativeCancel(long nativeContext);
85 
nativeCancelAnnouncement(long nativeContext)86     private native void nativeCancelAnnouncement(long nativeContext);
87 
nativeStartBackgroundScan(long nativeContext)88     private native boolean nativeStartBackgroundScan(long nativeContext);
nativeGetProgramList(long nativeContext, Map<String, String> vendorFilter)89     private native List<RadioManager.ProgramInfo> nativeGetProgramList(long nativeContext,
90             Map<String, String> vendorFilter);
91 
nativeGetImage(long nativeContext, int id)92     private native byte[] nativeGetImage(long nativeContext, int id);
93 
nativeIsAnalogForced(long nativeContext)94     private native boolean nativeIsAnalogForced(long nativeContext);
nativeSetAnalogForced(long nativeContext, boolean isForced)95     private native void nativeSetAnalogForced(long nativeContext, boolean isForced);
96 
97     @Override
close()98     public void close() {
99         synchronized (mLock) {
100             if (mIsClosed) return;
101             mIsClosed = true;
102             mTunerCallback.detach();
103             mClientCallback.asBinder().unlinkToDeath(mDeathRecipient, 0);
104             nativeClose(mNativeContext);
105         }
106     }
107 
108     @Override
isClosed()109     public boolean isClosed() {
110         return mIsClosed;
111     }
112 
checkNotClosedLocked()113     private void checkNotClosedLocked() {
114         if (mIsClosed) {
115             throw new IllegalStateException("Tuner is closed, no further operations are allowed");
116         }
117     }
118 
checkConfiguredLocked()119     private boolean checkConfiguredLocked() {
120         if (mTunerCallback.isInitialConfigurationDone()) return true;
121         Slog.w(TAG, "Initial configuration is still pending, skipping the operation");
122         return false;
123     }
124 
125     @Override
setConfiguration(RadioManager.BandConfig config)126     public void setConfiguration(RadioManager.BandConfig config) {
127         if (config == null) {
128             throw new IllegalArgumentException("The argument must not be a null pointer");
129         }
130         synchronized (mLock) {
131             checkNotClosedLocked();
132             nativeSetConfiguration(mNativeContext, config);
133             mRegion = config.getRegion();
134         }
135     }
136 
137     @Override
getConfiguration()138     public RadioManager.BandConfig getConfiguration() {
139         synchronized (mLock) {
140             checkNotClosedLocked();
141             return nativeGetConfiguration(mNativeContext, mRegion);
142         }
143     }
144 
145     @Override
setMuted(boolean mute)146     public void setMuted(boolean mute) {
147         if (!mWithAudio) {
148             throw new IllegalStateException("Can't operate on mute - no audio requested");
149         }
150         synchronized (mLock) {
151             checkNotClosedLocked();
152             if (mIsMuted == mute) return;
153             mIsMuted = mute;
154             Slog.w(TAG, "Mute via RadioService is not implemented - please handle it via app");
155         }
156     }
157 
158     @Override
isMuted()159     public boolean isMuted() {
160         if (!mWithAudio) {
161             Slog.w(TAG, "Tuner did not request audio, pretending it was muted");
162             return true;
163         }
164         synchronized (mLock) {
165             checkNotClosedLocked();
166             return mIsMuted;
167         }
168     }
169 
170     @Override
step(boolean directionDown, boolean skipSubChannel)171     public void step(boolean directionDown, boolean skipSubChannel) {
172         synchronized (mLock) {
173             checkNotClosedLocked();
174             if (!checkConfiguredLocked()) return;
175             nativeStep(mNativeContext, directionDown, skipSubChannel);
176         }
177     }
178 
179     @Override
scan(boolean directionDown, boolean skipSubChannel)180     public void scan(boolean directionDown, boolean skipSubChannel) {
181         synchronized (mLock) {
182             checkNotClosedLocked();
183             if (!checkConfiguredLocked()) return;
184             nativeScan(mNativeContext, directionDown, skipSubChannel);
185         }
186     }
187 
188     @Override
tune(ProgramSelector selector)189     public void tune(ProgramSelector selector) {
190         if (selector == null) {
191             throw new IllegalArgumentException("The argument must not be a null pointer");
192         }
193         Slog.i(TAG, "Tuning to " + selector);
194         synchronized (mLock) {
195             checkNotClosedLocked();
196             if (!checkConfiguredLocked()) return;
197             nativeTune(mNativeContext, selector);
198         }
199     }
200 
201     @Override
cancel()202     public void cancel() {
203         synchronized (mLock) {
204             checkNotClosedLocked();
205             nativeCancel(mNativeContext);
206         }
207     }
208 
209     @Override
cancelAnnouncement()210     public void cancelAnnouncement() {
211         synchronized (mLock) {
212             checkNotClosedLocked();
213             nativeCancelAnnouncement(mNativeContext);
214         }
215     }
216 
217     @Override
getImage(int id)218     public Bitmap getImage(int id) {
219         if (id == 0) {
220             throw new IllegalArgumentException("Image ID is missing");
221         }
222 
223         byte[] rawImage;
224         synchronized (mLock) {
225             rawImage = nativeGetImage(mNativeContext, id);
226         }
227         if (rawImage == null || rawImage.length == 0) {
228             return null;
229         }
230 
231         return BitmapFactory.decodeByteArray(rawImage, 0, rawImage.length);
232     }
233 
234     @Override
startBackgroundScan()235     public boolean startBackgroundScan() {
236         synchronized (mLock) {
237             checkNotClosedLocked();
238             return nativeStartBackgroundScan(mNativeContext);
239         }
240     }
241 
getProgramList(Map vendorFilter)242     List<RadioManager.ProgramInfo> getProgramList(Map vendorFilter) {
243         Map<String, String> sFilter = vendorFilter;
244         synchronized (mLock) {
245             checkNotClosedLocked();
246             List<RadioManager.ProgramInfo> list = nativeGetProgramList(mNativeContext, sFilter);
247             if (list == null) {
248                 throw new IllegalStateException("Program list is not ready");
249             }
250             return list;
251         }
252     }
253 
254     @Override
startProgramListUpdates(ProgramList.Filter filter)255     public void startProgramListUpdates(ProgramList.Filter filter) {
256         mTunerCallback.startProgramListUpdates(filter);
257     }
258 
259     @Override
stopProgramListUpdates()260     public void stopProgramListUpdates() {
261         mTunerCallback.stopProgramListUpdates();
262     }
263 
264     @Override
isConfigFlagSupported(int flag)265     public boolean isConfigFlagSupported(int flag) {
266         return flag == RadioManager.CONFIG_FORCE_ANALOG;
267     }
268 
269     @Override
isConfigFlagSet(int flag)270     public boolean isConfigFlagSet(int flag) {
271         if (flag == RadioManager.CONFIG_FORCE_ANALOG) {
272             synchronized (mLock) {
273                 checkNotClosedLocked();
274                 return nativeIsAnalogForced(mNativeContext);
275             }
276         }
277         throw new UnsupportedOperationException("Not supported by HAL 1.x");
278     }
279 
280     @Override
setConfigFlag(int flag, boolean value)281     public void setConfigFlag(int flag, boolean value) {
282         if (flag == RadioManager.CONFIG_FORCE_ANALOG) {
283             synchronized (mLock) {
284                 checkNotClosedLocked();
285                 nativeSetAnalogForced(mNativeContext, value);
286                 return;
287             }
288         }
289         throw new UnsupportedOperationException("Not supported by HAL 1.x");
290     }
291 
292     @Override
setParameters(Map<String, String> parameters)293     public Map<String, String> setParameters(Map<String, String> parameters) {
294         throw new UnsupportedOperationException("Not supported by HAL 1.x");
295     }
296 
297     @Override
getParameters(List<String> keys)298     public Map<String, String> getParameters(List<String> keys) {
299         throw new UnsupportedOperationException("Not supported by HAL 1.x");
300     }
301 }
302