blob: e5090ed5b715b776ef51dd9897e86d8de989f72b [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 Wasilczyk8cfb0e82017-07-12 13:59:20 -070024import android.hardware.radio.ProgramSelector;
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -070025import android.hardware.radio.RadioManager;
Tomasz Wasilczyke8f4b342017-06-19 10:48:16 -070026import android.os.IBinder;
27import android.os.RemoteException;
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -070028import android.util.Slog;
29
Tomasz Wasilczyk8e932c62017-11-17 16:18:40 +000030import java.util.Collections;
Tomasz Wasilczykd3d53f62017-05-15 12:55:28 -070031import java.util.List;
Tomasz Wasilczyk0f1776d2017-08-03 11:03:49 -070032import java.util.Map;
Tomasz Wasilczyk8e932c62017-11-17 16:18:40 +000033import java.util.Objects;
Tomasz Wasilczykd3d53f62017-05-15 12:55:28 -070034
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -070035class Tuner extends ITuner.Stub {
Tomasz Wasilczyk6b4b6462017-07-19 10:52:28 -070036 private static final String TAG = "BroadcastRadioService.Tuner";
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -070037
38 /**
39 * This field is used by native code, do not access or modify.
40 */
Tomasz Wasilczyk21348172017-04-20 14:02:42 -070041 private final long mNativeContext;
42
Tomasz Wasilczyk8b6db4f2017-05-01 09:28:36 -070043 private final Object mLock = new Object();
Tomasz Wasilczyke8f4b342017-06-19 10:48:16 -070044 @NonNull private final TunerCallback mTunerCallback;
45 @NonNull private final ITunerCallback mClientCallback;
46 @NonNull private final IBinder.DeathRecipient mDeathRecipient;
47
Tomasz Wasilczykf13b8412017-05-09 11:54:35 -070048 private boolean mIsClosed = false;
Tomasz Wasilczyk9fa02872017-05-03 09:09:57 -070049 private boolean mIsMuted = false;
Tomasz Wasilczyk7143f5c2017-06-16 11:18:36 -070050 private int mRegion; // TODO(b/62710330): find better solution to handle regions
Tomasz Wasilczyk9fa02872017-05-03 09:09:57 -070051 private final boolean mWithAudio;
Tomasz Wasilczyk31c8df02017-04-26 14:47:08 -070052
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -070053 Tuner(@NonNull ITunerCallback clientCallback, int halRev,
54 int region, boolean withAudio, int band) {
Tomasz Wasilczyke8f4b342017-06-19 10:48:16 -070055 mClientCallback = clientCallback;
Tomasz Wasilczykf13b8412017-05-09 11:54:35 -070056 mTunerCallback = new TunerCallback(this, clientCallback, halRev);
Tomasz Wasilczyk31c8df02017-04-26 14:47:08 -070057 mRegion = region;
Tomasz Wasilczyk9fa02872017-05-03 09:09:57 -070058 mWithAudio = withAudio;
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -070059 mNativeContext = nativeInit(halRev, withAudio, band);
Tomasz Wasilczyke8f4b342017-06-19 10:48:16 -070060 mDeathRecipient = this::close;
61 try {
62 mClientCallback.asBinder().linkToDeath(mDeathRecipient, 0);
63 } catch (RemoteException ex) {
64 close();
65 }
Tomasz Wasilczyk21348172017-04-20 14:02:42 -070066 }
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -070067
68 @Override
69 protected void finalize() throws Throwable {
70 nativeFinalize(mNativeContext);
71 super.finalize();
72 }
73
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -070074 private native long nativeInit(int halRev, boolean withAudio, int band);
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -070075 private native void nativeFinalize(long nativeContext);
Tomasz Wasilczyk21348172017-04-20 14:02:42 -070076 private native void nativeClose(long nativeContext);
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -070077
Tomasz Wasilczyk8b6db4f2017-05-01 09:28:36 -070078 private native void nativeSetConfiguration(long nativeContext,
79 @NonNull RadioManager.BandConfig config);
80 private native RadioManager.BandConfig nativeGetConfiguration(long nativeContext, int region);
81
Tomasz Wasilczykb1a6fea2017-09-12 10:21:16 -070082 private native void nativeSetMuted(long nativeContext, boolean mute);
83
Tomasz Wasilczyk23837932017-05-05 08:42:10 -070084 private native void nativeStep(long nativeContext, boolean directionDown, boolean skipSubChannel);
85 private native void nativeScan(long nativeContext, boolean directionDown, boolean skipSubChannel);
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -070086 private native void nativeTune(long nativeContext, @NonNull ProgramSelector selector);
Tomasz Wasilczyk23837932017-05-05 08:42:10 -070087 private native void nativeCancel(long nativeContext);
88
Tomasz Wasilczykc4cd8232017-07-14 10:46:15 -070089 private native void nativeCancelAnnouncement(long nativeContext);
90
Tomasz Wasilczyk37d986d2017-05-08 10:41:32 -070091 private native RadioManager.ProgramInfo nativeGetProgramInformation(long nativeContext);
Tomasz Wasilczykd3d53f62017-05-15 12:55:28 -070092 private native boolean nativeStartBackgroundScan(long nativeContext);
93 private native List<RadioManager.ProgramInfo> nativeGetProgramList(long nativeContext,
Tomasz Wasilczyk0f1776d2017-08-03 11:03:49 -070094 Map<String, String> vendorFilter);
Tomasz Wasilczykd3d53f62017-05-15 12:55:28 -070095
Tomasz Wasilczyk4482b142017-07-17 13:57:12 -070096 private native byte[] nativeGetImage(long nativeContext, int id);
97
Tomasz Wasilczykd3d53f62017-05-15 12:55:28 -070098 private native boolean nativeIsAnalogForced(long nativeContext);
99 private native void nativeSetAnalogForced(long nativeContext, boolean isForced);
Tomasz Wasilczyk37d986d2017-05-08 10:41:32 -0700100
Tomasz Wasilczyk8e932c62017-11-17 16:18:40 +0000101 private native Map<String, String> nativeSetParameters(long nativeContext,
102 Map<String, String> parameters);
103 private native Map<String, String> nativeGetParameters(long nativeContext, List<String> keys);
104
Tomasz Wasilczyk39ac2142017-05-17 14:55:17 -0700105 private native boolean nativeIsAntennaConnected(long nativeContext);
106
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -0700107 @Override
Tomasz Wasilczyk21348172017-04-20 14:02:42 -0700108 public void close() {
Tomasz Wasilczyk8b6db4f2017-05-01 09:28:36 -0700109 synchronized (mLock) {
Tomasz Wasilczykf13b8412017-05-09 11:54:35 -0700110 if (mIsClosed) return;
Tomasz Wasilczyk468a53b2017-06-19 15:19:14 -0700111 mIsClosed = true;
Tomasz Wasilczykf13b8412017-05-09 11:54:35 -0700112 mTunerCallback.detach();
Tomasz Wasilczyke8f4b342017-06-19 10:48:16 -0700113 mClientCallback.asBinder().unlinkToDeath(mDeathRecipient, 0);
Tomasz Wasilczyk8b6db4f2017-05-01 09:28:36 -0700114 nativeClose(mNativeContext);
Tomasz Wasilczykf13b8412017-05-09 11:54:35 -0700115 }
116 }
117
Tomasz Wasilczyk468a53b2017-06-19 15:19:14 -0700118 @Override
119 public boolean isClosed() {
120 return mIsClosed;
121 }
122
Tomasz Wasilczykf13b8412017-05-09 11:54:35 -0700123 private void checkNotClosedLocked() {
124 if (mIsClosed) {
125 throw new IllegalStateException("Tuner is closed, no further operations are allowed");
Tomasz Wasilczyk8b6db4f2017-05-01 09:28:36 -0700126 }
127 }
128
129 @Override
130 public void setConfiguration(RadioManager.BandConfig config) {
131 if (config == null) {
132 throw new IllegalArgumentException("The argument must not be a null pointer");
133 }
134 synchronized (mLock) {
Tomasz Wasilczykf13b8412017-05-09 11:54:35 -0700135 checkNotClosedLocked();
Tomasz Wasilczyk8b6db4f2017-05-01 09:28:36 -0700136 nativeSetConfiguration(mNativeContext, config);
137 mRegion = config.getRegion();
138 }
139 }
140
141 @Override
142 public RadioManager.BandConfig getConfiguration() {
143 synchronized (mLock) {
Tomasz Wasilczykf13b8412017-05-09 11:54:35 -0700144 checkNotClosedLocked();
Tomasz Wasilczyk8b6db4f2017-05-01 09:28:36 -0700145 return nativeGetConfiguration(mNativeContext, mRegion);
146 }
Tomasz Wasilczyk21348172017-04-20 14:02:42 -0700147 }
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -0700148
149 @Override
Tomasz Wasilczyk9fa02872017-05-03 09:09:57 -0700150 public void setMuted(boolean mute) {
151 if (!mWithAudio) {
152 throw new IllegalStateException("Can't operate on mute - no audio requested");
153 }
154 synchronized (mLock) {
Tomasz Wasilczykf13b8412017-05-09 11:54:35 -0700155 checkNotClosedLocked();
Tomasz Wasilczyk9fa02872017-05-03 09:09:57 -0700156 if (mIsMuted == mute) return;
157 mIsMuted = mute;
158
Tomasz Wasilczykb1a6fea2017-09-12 10:21:16 -0700159 nativeSetMuted(mNativeContext, mute);
Tomasz Wasilczyk9fa02872017-05-03 09:09:57 -0700160 }
161 }
162
163 @Override
164 public boolean isMuted() {
165 if (!mWithAudio) {
166 Slog.w(TAG, "Tuner did not request audio, pretending it was muted");
167 return true;
168 }
169 synchronized (mLock) {
Tomasz Wasilczykf13b8412017-05-09 11:54:35 -0700170 checkNotClosedLocked();
Tomasz Wasilczyk9fa02872017-05-03 09:09:57 -0700171 return mIsMuted;
172 }
173 }
Tomasz Wasilczyk23837932017-05-05 08:42:10 -0700174
175 @Override
176 public void step(boolean directionDown, boolean skipSubChannel) {
177 synchronized (mLock) {
Tomasz Wasilczykf13b8412017-05-09 11:54:35 -0700178 checkNotClosedLocked();
Tomasz Wasilczyk23837932017-05-05 08:42:10 -0700179 nativeStep(mNativeContext, directionDown, skipSubChannel);
180 }
181 }
182
183 @Override
184 public void scan(boolean directionDown, boolean skipSubChannel) {
185 synchronized (mLock) {
Tomasz Wasilczykf13b8412017-05-09 11:54:35 -0700186 checkNotClosedLocked();
Tomasz Wasilczyk23837932017-05-05 08:42:10 -0700187 nativeScan(mNativeContext, directionDown, skipSubChannel);
188 }
189 }
190
191 @Override
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -0700192 public void tune(ProgramSelector selector) {
193 if (selector == null) {
194 throw new IllegalArgumentException("The argument must not be a null pointer");
195 }
196 Slog.i(TAG, "Tuning to " + selector);
Tomasz Wasilczyk23837932017-05-05 08:42:10 -0700197 synchronized (mLock) {
Tomasz Wasilczykf13b8412017-05-09 11:54:35 -0700198 checkNotClosedLocked();
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -0700199 nativeTune(mNativeContext, selector);
Tomasz Wasilczyk23837932017-05-05 08:42:10 -0700200 }
201 }
202
203 @Override
204 public void cancel() {
205 synchronized (mLock) {
Tomasz Wasilczykf13b8412017-05-09 11:54:35 -0700206 checkNotClosedLocked();
Tomasz Wasilczyk23837932017-05-05 08:42:10 -0700207 nativeCancel(mNativeContext);
208 }
209 }
Tomasz Wasilczyk37d986d2017-05-08 10:41:32 -0700210
211 @Override
Tomasz Wasilczykc4cd8232017-07-14 10:46:15 -0700212 public void cancelAnnouncement() {
213 synchronized (mLock) {
214 checkNotClosedLocked();
215 nativeCancelAnnouncement(mNativeContext);
216 }
217 }
218
219 @Override
Tomasz Wasilczyk37d986d2017-05-08 10:41:32 -0700220 public RadioManager.ProgramInfo getProgramInformation() {
221 synchronized (mLock) {
Tomasz Wasilczykf13b8412017-05-09 11:54:35 -0700222 checkNotClosedLocked();
Tomasz Wasilczyk37d986d2017-05-08 10:41:32 -0700223 return nativeGetProgramInformation(mNativeContext);
224 }
225 }
Tomasz Wasilczykd3d53f62017-05-15 12:55:28 -0700226
Tomasz Wasilczyk39ac2142017-05-17 14:55:17 -0700227 @Override
Tomasz Wasilczyk4482b142017-07-17 13:57:12 -0700228 public Bitmap getImage(int id) {
229 if (id == 0) {
230 throw new IllegalArgumentException("Image ID is missing");
231 }
232
233 byte[] rawImage;
234 synchronized (mLock) {
235 rawImage = nativeGetImage(mNativeContext, id);
236 }
237 if (rawImage == null || rawImage.length == 0) {
238 return null;
239 }
240
241 return BitmapFactory.decodeByteArray(rawImage, 0, rawImage.length);
242 }
243
244 @Override
Tomasz Wasilczykd3d53f62017-05-15 12:55:28 -0700245 public boolean startBackgroundScan() {
246 synchronized (mLock) {
247 checkNotClosedLocked();
248 return nativeStartBackgroundScan(mNativeContext);
249 }
250 }
251
Tomasz Wasilczyk39ac2142017-05-17 14:55:17 -0700252 @Override
Tomasz Wasilczyk0f1776d2017-08-03 11:03:49 -0700253 public List<RadioManager.ProgramInfo> getProgramList(Map vendorFilter) {
254 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 Wasilczykce40fe92018-01-04 20:52:39 -0800266 public boolean isConfigFlagSupported(int flag) {
267 return flag == RadioManager.CONFIG_FORCE_ANALOG;
Tomasz Wasilczykd3d53f62017-05-15 12:55:28 -0700268 }
269
Tomasz Wasilczyk39ac2142017-05-17 14:55:17 -0700270 @Override
Tomasz Wasilczykce40fe92018-01-04 20:52:39 -0800271 public boolean isConfigFlagSet(int flag) {
272 if (flag == RadioManager.CONFIG_FORCE_ANALOG) {
273 synchronized (mLock) {
274 checkNotClosedLocked();
275 return nativeIsAnalogForced(mNativeContext);
276 }
Tomasz Wasilczykd3d53f62017-05-15 12:55:28 -0700277 }
Tomasz Wasilczykce40fe92018-01-04 20:52:39 -0800278 throw new UnsupportedOperationException("Not supported by HAL 1.x");
279 }
280
281 @Override
282 public void setConfigFlag(int flag, boolean value) {
283 if (flag == RadioManager.CONFIG_FORCE_ANALOG) {
284 synchronized (mLock) {
285 checkNotClosedLocked();
286 nativeSetAnalogForced(mNativeContext, value);
287 return;
288 }
289 }
290 throw new UnsupportedOperationException("Not supported by HAL 1.x");
Tomasz Wasilczykd3d53f62017-05-15 12:55:28 -0700291 }
Tomasz Wasilczyk39ac2142017-05-17 14:55:17 -0700292
293 @Override
Tomasz Wasilczyk8e932c62017-11-17 16:18:40 +0000294 public Map setParameters(Map parameters) {
295 Map<String, String> results;
296 synchronized (mLock) {
297 checkNotClosedLocked();
298 results = nativeSetParameters(mNativeContext, Objects.requireNonNull(parameters));
299 }
300 if (results == null) return Collections.emptyMap();
301 return results;
302 }
303
304 @Override
305 public Map getParameters(List<String> keys) {
306 if (keys == null) {
307 throw new IllegalArgumentException("The argument must not be a null pointer");
308 }
309 Map<String, String> results;
310 synchronized (mLock) {
311 checkNotClosedLocked();
312 results = nativeGetParameters(mNativeContext, keys);
313 }
314 if (results == null) return Collections.emptyMap();
315 return results;
316 }
317
318 @Override
Tomasz Wasilczyk39ac2142017-05-17 14:55:17 -0700319 public boolean isAntennaConnected() {
320 synchronized (mLock) {
321 checkNotClosedLocked();
322 return nativeIsAntennaConnected(mNativeContext);
323 }
324 }
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -0700325}