blob: f9b35f53d4d540f9ada16dba65f594e4c7f9ba34 [file] [log] [blame]
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -07001/**
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
Tomasz Wasilczykdf013262017-12-13 11:47:20 -080017package com.android.server.broadcastradio.hal1;
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -070018
Tomasz Wasilczyk21348172017-04-20 14:02:42 -070019import android.annotation.NonNull;
Tomasz Wasilczyk4482b142017-07-17 13:57:12 -070020import android.graphics.Bitmap;
21import android.graphics.BitmapFactory;
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -070022import android.hardware.radio.ITuner;
Tomasz Wasilczyk21348172017-04-20 14:02:42 -070023import android.hardware.radio.ITunerCallback;
Tomasz Wasilczyk436128f2018-01-08 16:46:09 -080024import android.hardware.radio.ProgramList;
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -070025import android.hardware.radio.ProgramSelector;
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -070026import android.hardware.radio.RadioManager;
Tomasz Wasilczyke8f4b342017-06-19 10:48:16 -070027import android.os.IBinder;
28import android.os.RemoteException;
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -070029import android.util.Slog;
30
Tomasz Wasilczyk8e932c62017-11-17 16:18:40 +000031import java.util.Collections;
Tomasz Wasilczykd3d53f62017-05-15 12:55:28 -070032import java.util.List;
Tomasz Wasilczyk0f1776d2017-08-03 11:03:49 -070033import java.util.Map;
Tomasz Wasilczyk8e932c62017-11-17 16:18:40 +000034import java.util.Objects;
Tomasz Wasilczykd3d53f62017-05-15 12:55:28 -070035
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -070036class Tuner extends ITuner.Stub {
Tomasz Wasilczyk6b4b6462017-07-19 10:52:28 -070037 private static final String TAG = "BroadcastRadioService.Tuner";
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -070038
39 /**
40 * This field is used by native code, do not access or modify.
41 */
Tomasz Wasilczyk21348172017-04-20 14:02:42 -070042 private final long mNativeContext;
43
Tomasz Wasilczyk8b6db4f2017-05-01 09:28:36 -070044 private final Object mLock = new Object();
Tomasz Wasilczyke8f4b342017-06-19 10:48:16 -070045 @NonNull private final TunerCallback mTunerCallback;
46 @NonNull private final ITunerCallback mClientCallback;
47 @NonNull private final IBinder.DeathRecipient mDeathRecipient;
48
Tomasz Wasilczykf13b8412017-05-09 11:54:35 -070049 private boolean mIsClosed = false;
Tomasz Wasilczyk9fa02872017-05-03 09:09:57 -070050 private boolean mIsMuted = false;
Tomasz Wasilczyk7143f5c2017-06-16 11:18:36 -070051 private int mRegion; // TODO(b/62710330): find better solution to handle regions
Tomasz Wasilczyk9fa02872017-05-03 09:09:57 -070052 private final boolean mWithAudio;
Tomasz Wasilczyk31c8df02017-04-26 14:47:08 -070053
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -070054 Tuner(@NonNull ITunerCallback clientCallback, int halRev,
55 int region, boolean withAudio, int band) {
Tomasz Wasilczyke8f4b342017-06-19 10:48:16 -070056 mClientCallback = clientCallback;
Tomasz Wasilczykf13b8412017-05-09 11:54:35 -070057 mTunerCallback = new TunerCallback(this, clientCallback, halRev);
Tomasz Wasilczyk31c8df02017-04-26 14:47:08 -070058 mRegion = region;
Tomasz Wasilczyk9fa02872017-05-03 09:09:57 -070059 mWithAudio = withAudio;
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -070060 mNativeContext = nativeInit(halRev, withAudio, band);
Tomasz Wasilczyke8f4b342017-06-19 10:48:16 -070061 mDeathRecipient = this::close;
62 try {
63 mClientCallback.asBinder().linkToDeath(mDeathRecipient, 0);
64 } catch (RemoteException ex) {
65 close();
66 }
Tomasz Wasilczyk21348172017-04-20 14:02:42 -070067 }
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -070068
69 @Override
70 protected void finalize() throws Throwable {
71 nativeFinalize(mNativeContext);
72 super.finalize();
73 }
74
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -070075 private native long nativeInit(int halRev, boolean withAudio, int band);
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -070076 private native void nativeFinalize(long nativeContext);
Tomasz Wasilczyk21348172017-04-20 14:02:42 -070077 private native void nativeClose(long nativeContext);
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -070078
Tomasz Wasilczyk8b6db4f2017-05-01 09:28:36 -070079 private native void nativeSetConfiguration(long nativeContext,
80 @NonNull RadioManager.BandConfig config);
81 private native RadioManager.BandConfig nativeGetConfiguration(long nativeContext, int region);
82
Tomasz Wasilczykb1a6fea2017-09-12 10:21:16 -070083 private native void nativeSetMuted(long nativeContext, boolean mute);
84
Tomasz Wasilczyk23837932017-05-05 08:42:10 -070085 private native void nativeStep(long nativeContext, boolean directionDown, boolean skipSubChannel);
86 private native void nativeScan(long nativeContext, boolean directionDown, boolean skipSubChannel);
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -070087 private native void nativeTune(long nativeContext, @NonNull ProgramSelector selector);
Tomasz Wasilczyk23837932017-05-05 08:42:10 -070088 private native void nativeCancel(long nativeContext);
89
Tomasz Wasilczykc4cd8232017-07-14 10:46:15 -070090 private native void nativeCancelAnnouncement(long nativeContext);
91
Tomasz Wasilczyk37d986d2017-05-08 10:41:32 -070092 private native RadioManager.ProgramInfo nativeGetProgramInformation(long nativeContext);
Tomasz Wasilczykd3d53f62017-05-15 12:55:28 -070093 private native boolean nativeStartBackgroundScan(long nativeContext);
94 private native List<RadioManager.ProgramInfo> nativeGetProgramList(long nativeContext,
Tomasz Wasilczyk0f1776d2017-08-03 11:03:49 -070095 Map<String, String> vendorFilter);
Tomasz Wasilczykd3d53f62017-05-15 12:55:28 -070096
Tomasz Wasilczyk4482b142017-07-17 13:57:12 -070097 private native byte[] nativeGetImage(long nativeContext, int id);
98
Tomasz Wasilczykd3d53f62017-05-15 12:55:28 -070099 private native boolean nativeIsAnalogForced(long nativeContext);
100 private native void nativeSetAnalogForced(long nativeContext, boolean isForced);
Tomasz Wasilczyk37d986d2017-05-08 10:41:32 -0700101
Tomasz Wasilczyk8e932c62017-11-17 16:18:40 +0000102 private native Map<String, String> nativeSetParameters(long nativeContext,
103 Map<String, String> parameters);
104 private native Map<String, String> nativeGetParameters(long nativeContext, List<String> keys);
105
Tomasz Wasilczyk39ac2142017-05-17 14:55:17 -0700106 private native boolean nativeIsAntennaConnected(long nativeContext);
107
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -0700108 @Override
Tomasz Wasilczyk21348172017-04-20 14:02:42 -0700109 public void close() {
Tomasz Wasilczyk8b6db4f2017-05-01 09:28:36 -0700110 synchronized (mLock) {
Tomasz Wasilczykf13b8412017-05-09 11:54:35 -0700111 if (mIsClosed) return;
Tomasz Wasilczyk468a53b2017-06-19 15:19:14 -0700112 mIsClosed = true;
Tomasz Wasilczykf13b8412017-05-09 11:54:35 -0700113 mTunerCallback.detach();
Tomasz Wasilczyke8f4b342017-06-19 10:48:16 -0700114 mClientCallback.asBinder().unlinkToDeath(mDeathRecipient, 0);
Tomasz Wasilczyk8b6db4f2017-05-01 09:28:36 -0700115 nativeClose(mNativeContext);
Tomasz Wasilczykf13b8412017-05-09 11:54:35 -0700116 }
117 }
118
Tomasz Wasilczyk468a53b2017-06-19 15:19:14 -0700119 @Override
120 public boolean isClosed() {
121 return mIsClosed;
122 }
123
Tomasz Wasilczykf13b8412017-05-09 11:54:35 -0700124 private void checkNotClosedLocked() {
125 if (mIsClosed) {
126 throw new IllegalStateException("Tuner is closed, no further operations are allowed");
Tomasz Wasilczyk8b6db4f2017-05-01 09:28:36 -0700127 }
128 }
129
130 @Override
131 public void setConfiguration(RadioManager.BandConfig config) {
132 if (config == null) {
133 throw new IllegalArgumentException("The argument must not be a null pointer");
134 }
135 synchronized (mLock) {
Tomasz Wasilczykf13b8412017-05-09 11:54:35 -0700136 checkNotClosedLocked();
Tomasz Wasilczyk8b6db4f2017-05-01 09:28:36 -0700137 nativeSetConfiguration(mNativeContext, config);
138 mRegion = config.getRegion();
139 }
140 }
141
142 @Override
143 public RadioManager.BandConfig getConfiguration() {
144 synchronized (mLock) {
Tomasz Wasilczykf13b8412017-05-09 11:54:35 -0700145 checkNotClosedLocked();
Tomasz Wasilczyk8b6db4f2017-05-01 09:28:36 -0700146 return nativeGetConfiguration(mNativeContext, mRegion);
147 }
Tomasz Wasilczyk21348172017-04-20 14:02:42 -0700148 }
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -0700149
150 @Override
Tomasz Wasilczyk9fa02872017-05-03 09:09:57 -0700151 public void setMuted(boolean mute) {
152 if (!mWithAudio) {
153 throw new IllegalStateException("Can't operate on mute - no audio requested");
154 }
155 synchronized (mLock) {
Tomasz Wasilczykf13b8412017-05-09 11:54:35 -0700156 checkNotClosedLocked();
Tomasz Wasilczyk9fa02872017-05-03 09:09:57 -0700157 if (mIsMuted == mute) return;
158 mIsMuted = mute;
159
Tomasz Wasilczykb1a6fea2017-09-12 10:21:16 -0700160 nativeSetMuted(mNativeContext, mute);
Tomasz Wasilczyk9fa02872017-05-03 09:09:57 -0700161 }
162 }
163
164 @Override
165 public boolean isMuted() {
166 if (!mWithAudio) {
167 Slog.w(TAG, "Tuner did not request audio, pretending it was muted");
168 return true;
169 }
170 synchronized (mLock) {
Tomasz Wasilczykf13b8412017-05-09 11:54:35 -0700171 checkNotClosedLocked();
Tomasz Wasilczyk9fa02872017-05-03 09:09:57 -0700172 return mIsMuted;
173 }
174 }
Tomasz Wasilczyk23837932017-05-05 08:42:10 -0700175
176 @Override
177 public void step(boolean directionDown, boolean skipSubChannel) {
178 synchronized (mLock) {
Tomasz Wasilczykf13b8412017-05-09 11:54:35 -0700179 checkNotClosedLocked();
Tomasz Wasilczyk23837932017-05-05 08:42:10 -0700180 nativeStep(mNativeContext, directionDown, skipSubChannel);
181 }
182 }
183
184 @Override
185 public void scan(boolean directionDown, boolean skipSubChannel) {
186 synchronized (mLock) {
Tomasz Wasilczykf13b8412017-05-09 11:54:35 -0700187 checkNotClosedLocked();
Tomasz Wasilczyk23837932017-05-05 08:42:10 -0700188 nativeScan(mNativeContext, directionDown, skipSubChannel);
189 }
190 }
191
192 @Override
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -0700193 public void tune(ProgramSelector selector) {
194 if (selector == null) {
195 throw new IllegalArgumentException("The argument must not be a null pointer");
196 }
197 Slog.i(TAG, "Tuning to " + selector);
Tomasz Wasilczyk23837932017-05-05 08:42:10 -0700198 synchronized (mLock) {
Tomasz Wasilczykf13b8412017-05-09 11:54:35 -0700199 checkNotClosedLocked();
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -0700200 nativeTune(mNativeContext, selector);
Tomasz Wasilczyk23837932017-05-05 08:42:10 -0700201 }
202 }
203
204 @Override
205 public void cancel() {
206 synchronized (mLock) {
Tomasz Wasilczykf13b8412017-05-09 11:54:35 -0700207 checkNotClosedLocked();
Tomasz Wasilczyk23837932017-05-05 08:42:10 -0700208 nativeCancel(mNativeContext);
209 }
210 }
Tomasz Wasilczyk37d986d2017-05-08 10:41:32 -0700211
212 @Override
Tomasz Wasilczykc4cd8232017-07-14 10:46:15 -0700213 public void cancelAnnouncement() {
214 synchronized (mLock) {
215 checkNotClosedLocked();
216 nativeCancelAnnouncement(mNativeContext);
217 }
218 }
219
220 @Override
Tomasz Wasilczyk37d986d2017-05-08 10:41:32 -0700221 public RadioManager.ProgramInfo getProgramInformation() {
222 synchronized (mLock) {
Tomasz Wasilczykf13b8412017-05-09 11:54:35 -0700223 checkNotClosedLocked();
Tomasz Wasilczyk37d986d2017-05-08 10:41:32 -0700224 return nativeGetProgramInformation(mNativeContext);
225 }
226 }
Tomasz Wasilczykd3d53f62017-05-15 12:55:28 -0700227
Tomasz Wasilczyk39ac2142017-05-17 14:55:17 -0700228 @Override
Tomasz Wasilczyk4482b142017-07-17 13:57:12 -0700229 public Bitmap getImage(int id) {
230 if (id == 0) {
231 throw new IllegalArgumentException("Image ID is missing");
232 }
233
234 byte[] rawImage;
235 synchronized (mLock) {
236 rawImage = nativeGetImage(mNativeContext, id);
237 }
238 if (rawImage == null || rawImage.length == 0) {
239 return null;
240 }
241
242 return BitmapFactory.decodeByteArray(rawImage, 0, rawImage.length);
243 }
244
245 @Override
Tomasz Wasilczykd3d53f62017-05-15 12:55:28 -0700246 public boolean startBackgroundScan() {
247 synchronized (mLock) {
248 checkNotClosedLocked();
249 return nativeStartBackgroundScan(mNativeContext);
250 }
251 }
252
Tomasz Wasilczyk436128f2018-01-08 16:46:09 -0800253 List<RadioManager.ProgramInfo> getProgramList(Map vendorFilter) {
Tomasz Wasilczyk0f1776d2017-08-03 11:03:49 -0700254 Map<String, String> sFilter = vendorFilter;
Tomasz Wasilczykd3d53f62017-05-15 12:55:28 -0700255 synchronized (mLock) {
256 checkNotClosedLocked();
Tomasz Wasilczyk0f1776d2017-08-03 11:03:49 -0700257 List<RadioManager.ProgramInfo> list = nativeGetProgramList(mNativeContext, sFilter);
Tomasz Wasilczykd3d53f62017-05-15 12:55:28 -0700258 if (list == null) {
259 throw new IllegalStateException("Program list is not ready");
260 }
261 return list;
262 }
263 }
264
Tomasz Wasilczyk39ac2142017-05-17 14:55:17 -0700265 @Override
Tomasz Wasilczyk436128f2018-01-08 16:46:09 -0800266 public void startProgramListUpdates(ProgramList.Filter filter) {
267 mTunerCallback.startProgramListUpdates(filter);
268 }
269
270 @Override
271 public void stopProgramListUpdates() {
272 mTunerCallback.stopProgramListUpdates();
273 }
274
275 @Override
Tomasz Wasilczykce40fe92018-01-04 20:52:39 -0800276 public boolean isConfigFlagSupported(int flag) {
277 return flag == RadioManager.CONFIG_FORCE_ANALOG;
Tomasz Wasilczykd3d53f62017-05-15 12:55:28 -0700278 }
279
Tomasz Wasilczyk39ac2142017-05-17 14:55:17 -0700280 @Override
Tomasz Wasilczykce40fe92018-01-04 20:52:39 -0800281 public boolean isConfigFlagSet(int flag) {
282 if (flag == RadioManager.CONFIG_FORCE_ANALOG) {
283 synchronized (mLock) {
284 checkNotClosedLocked();
285 return nativeIsAnalogForced(mNativeContext);
286 }
Tomasz Wasilczykd3d53f62017-05-15 12:55:28 -0700287 }
Tomasz Wasilczykce40fe92018-01-04 20:52:39 -0800288 throw new UnsupportedOperationException("Not supported by HAL 1.x");
289 }
290
291 @Override
292 public void setConfigFlag(int flag, boolean value) {
293 if (flag == RadioManager.CONFIG_FORCE_ANALOG) {
294 synchronized (mLock) {
295 checkNotClosedLocked();
296 nativeSetAnalogForced(mNativeContext, value);
297 return;
298 }
299 }
300 throw new UnsupportedOperationException("Not supported by HAL 1.x");
Tomasz Wasilczykd3d53f62017-05-15 12:55:28 -0700301 }
Tomasz Wasilczyk39ac2142017-05-17 14:55:17 -0700302
303 @Override
Tomasz Wasilczyk8e932c62017-11-17 16:18:40 +0000304 public Map setParameters(Map parameters) {
305 Map<String, String> results;
306 synchronized (mLock) {
307 checkNotClosedLocked();
308 results = nativeSetParameters(mNativeContext, Objects.requireNonNull(parameters));
309 }
310 if (results == null) return Collections.emptyMap();
311 return results;
312 }
313
314 @Override
315 public Map getParameters(List<String> keys) {
316 if (keys == null) {
317 throw new IllegalArgumentException("The argument must not be a null pointer");
318 }
319 Map<String, String> results;
320 synchronized (mLock) {
321 checkNotClosedLocked();
322 results = nativeGetParameters(mNativeContext, keys);
323 }
324 if (results == null) return Collections.emptyMap();
325 return results;
326 }
327
328 @Override
Tomasz Wasilczyk39ac2142017-05-17 14:55:17 -0700329 public boolean isAntennaConnected() {
330 synchronized (mLock) {
331 checkNotClosedLocked();
332 return nativeIsAntennaConnected(mNativeContext);
333 }
334 }
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -0700335}