blob: e77cb7a7ad94eb5c146683024d771e19733a46e6 [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 Wasilczyk3b4465e2018-01-14 21:47:44 -080051 private int mRegion;
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 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 Wasilczykd7c21d32017-04-17 17:02:06 -0700105 @Override
Tomasz Wasilczyk21348172017-04-20 14:02:42 -0700106 public void close() {
Tomasz Wasilczyk8b6db4f2017-05-01 09:28:36 -0700107 synchronized (mLock) {
Tomasz Wasilczykf13b8412017-05-09 11:54:35 -0700108 if (mIsClosed) return;
Tomasz Wasilczyk468a53b2017-06-19 15:19:14 -0700109 mIsClosed = true;
Tomasz Wasilczykf13b8412017-05-09 11:54:35 -0700110 mTunerCallback.detach();
Tomasz Wasilczyke8f4b342017-06-19 10:48:16 -0700111 mClientCallback.asBinder().unlinkToDeath(mDeathRecipient, 0);
Tomasz Wasilczyk8b6db4f2017-05-01 09:28:36 -0700112 nativeClose(mNativeContext);
Tomasz Wasilczykf13b8412017-05-09 11:54:35 -0700113 }
114 }
115
Tomasz Wasilczyk468a53b2017-06-19 15:19:14 -0700116 @Override
117 public boolean isClosed() {
118 return mIsClosed;
119 }
120
Tomasz Wasilczykf13b8412017-05-09 11:54:35 -0700121 private void checkNotClosedLocked() {
122 if (mIsClosed) {
123 throw new IllegalStateException("Tuner is closed, no further operations are allowed");
Tomasz Wasilczyk8b6db4f2017-05-01 09:28:36 -0700124 }
125 }
126
127 @Override
128 public void setConfiguration(RadioManager.BandConfig config) {
129 if (config == null) {
130 throw new IllegalArgumentException("The argument must not be a null pointer");
131 }
132 synchronized (mLock) {
Tomasz Wasilczykf13b8412017-05-09 11:54:35 -0700133 checkNotClosedLocked();
Tomasz Wasilczyk8b6db4f2017-05-01 09:28:36 -0700134 nativeSetConfiguration(mNativeContext, config);
135 mRegion = config.getRegion();
136 }
137 }
138
139 @Override
140 public RadioManager.BandConfig getConfiguration() {
141 synchronized (mLock) {
Tomasz Wasilczykf13b8412017-05-09 11:54:35 -0700142 checkNotClosedLocked();
Tomasz Wasilczyk8b6db4f2017-05-01 09:28:36 -0700143 return nativeGetConfiguration(mNativeContext, mRegion);
144 }
Tomasz Wasilczyk21348172017-04-20 14:02:42 -0700145 }
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -0700146
147 @Override
Tomasz Wasilczyk9fa02872017-05-03 09:09:57 -0700148 public void setMuted(boolean mute) {
149 if (!mWithAudio) {
150 throw new IllegalStateException("Can't operate on mute - no audio requested");
151 }
152 synchronized (mLock) {
Tomasz Wasilczykf13b8412017-05-09 11:54:35 -0700153 checkNotClosedLocked();
Tomasz Wasilczyk9fa02872017-05-03 09:09:57 -0700154 if (mIsMuted == mute) return;
155 mIsMuted = mute;
156
Tomasz Wasilczykb1a6fea2017-09-12 10:21:16 -0700157 nativeSetMuted(mNativeContext, mute);
Tomasz Wasilczyk9fa02872017-05-03 09:09:57 -0700158 }
159 }
160
161 @Override
162 public boolean isMuted() {
163 if (!mWithAudio) {
164 Slog.w(TAG, "Tuner did not request audio, pretending it was muted");
165 return true;
166 }
167 synchronized (mLock) {
Tomasz Wasilczykf13b8412017-05-09 11:54:35 -0700168 checkNotClosedLocked();
Tomasz Wasilczyk9fa02872017-05-03 09:09:57 -0700169 return mIsMuted;
170 }
171 }
Tomasz Wasilczyk23837932017-05-05 08:42:10 -0700172
173 @Override
174 public void step(boolean directionDown, boolean skipSubChannel) {
175 synchronized (mLock) {
Tomasz Wasilczykf13b8412017-05-09 11:54:35 -0700176 checkNotClosedLocked();
Tomasz Wasilczyk23837932017-05-05 08:42:10 -0700177 nativeStep(mNativeContext, directionDown, skipSubChannel);
178 }
179 }
180
181 @Override
182 public void scan(boolean directionDown, boolean skipSubChannel) {
183 synchronized (mLock) {
Tomasz Wasilczykf13b8412017-05-09 11:54:35 -0700184 checkNotClosedLocked();
Tomasz Wasilczyk23837932017-05-05 08:42:10 -0700185 nativeScan(mNativeContext, directionDown, skipSubChannel);
186 }
187 }
188
189 @Override
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -0700190 public void tune(ProgramSelector selector) {
191 if (selector == null) {
192 throw new IllegalArgumentException("The argument must not be a null pointer");
193 }
194 Slog.i(TAG, "Tuning to " + selector);
Tomasz Wasilczyk23837932017-05-05 08:42:10 -0700195 synchronized (mLock) {
Tomasz Wasilczykf13b8412017-05-09 11:54:35 -0700196 checkNotClosedLocked();
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -0700197 nativeTune(mNativeContext, selector);
Tomasz Wasilczyk23837932017-05-05 08:42:10 -0700198 }
199 }
200
201 @Override
202 public void cancel() {
203 synchronized (mLock) {
Tomasz Wasilczykf13b8412017-05-09 11:54:35 -0700204 checkNotClosedLocked();
Tomasz Wasilczyk23837932017-05-05 08:42:10 -0700205 nativeCancel(mNativeContext);
206 }
207 }
Tomasz Wasilczyk37d986d2017-05-08 10:41:32 -0700208
209 @Override
Tomasz Wasilczykc4cd8232017-07-14 10:46:15 -0700210 public void cancelAnnouncement() {
211 synchronized (mLock) {
212 checkNotClosedLocked();
213 nativeCancelAnnouncement(mNativeContext);
214 }
215 }
216
217 @Override
Tomasz Wasilczyk4482b142017-07-17 13:57:12 -0700218 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
Tomasz Wasilczykd3d53f62017-05-15 12:55:28 -0700235 public boolean startBackgroundScan() {
236 synchronized (mLock) {
237 checkNotClosedLocked();
238 return nativeStartBackgroundScan(mNativeContext);
239 }
240 }
241
Tomasz Wasilczyk436128f2018-01-08 16:46:09 -0800242 List<RadioManager.ProgramInfo> getProgramList(Map vendorFilter) {
Tomasz Wasilczyk0f1776d2017-08-03 11:03:49 -0700243 Map<String, String> sFilter = vendorFilter;
Tomasz Wasilczykd3d53f62017-05-15 12:55:28 -0700244 synchronized (mLock) {
245 checkNotClosedLocked();
Tomasz Wasilczyk0f1776d2017-08-03 11:03:49 -0700246 List<RadioManager.ProgramInfo> list = nativeGetProgramList(mNativeContext, sFilter);
Tomasz Wasilczykd3d53f62017-05-15 12:55:28 -0700247 if (list == null) {
248 throw new IllegalStateException("Program list is not ready");
249 }
250 return list;
251 }
252 }
253
Tomasz Wasilczyk39ac2142017-05-17 14:55:17 -0700254 @Override
Tomasz Wasilczyk436128f2018-01-08 16:46:09 -0800255 public void startProgramListUpdates(ProgramList.Filter filter) {
256 mTunerCallback.startProgramListUpdates(filter);
257 }
258
259 @Override
260 public void stopProgramListUpdates() {
261 mTunerCallback.stopProgramListUpdates();
262 }
263
264 @Override
Tomasz Wasilczykce40fe92018-01-04 20:52:39 -0800265 public boolean isConfigFlagSupported(int flag) {
266 return flag == RadioManager.CONFIG_FORCE_ANALOG;
Tomasz Wasilczykd3d53f62017-05-15 12:55:28 -0700267 }
268
Tomasz Wasilczyk39ac2142017-05-17 14:55:17 -0700269 @Override
Tomasz Wasilczykce40fe92018-01-04 20:52:39 -0800270 public boolean isConfigFlagSet(int flag) {
271 if (flag == RadioManager.CONFIG_FORCE_ANALOG) {
272 synchronized (mLock) {
273 checkNotClosedLocked();
274 return nativeIsAnalogForced(mNativeContext);
275 }
Tomasz Wasilczykd3d53f62017-05-15 12:55:28 -0700276 }
Tomasz Wasilczykce40fe92018-01-04 20:52:39 -0800277 throw new UnsupportedOperationException("Not supported by HAL 1.x");
278 }
279
280 @Override
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");
Tomasz Wasilczykd3d53f62017-05-15 12:55:28 -0700290 }
Tomasz Wasilczyk39ac2142017-05-17 14:55:17 -0700291
292 @Override
Tomasz Wasilczyk8e932c62017-11-17 16:18:40 +0000293 public Map setParameters(Map parameters) {
294 Map<String, String> results;
295 synchronized (mLock) {
296 checkNotClosedLocked();
297 results = nativeSetParameters(mNativeContext, Objects.requireNonNull(parameters));
298 }
299 if (results == null) return Collections.emptyMap();
300 return results;
301 }
302
303 @Override
304 public Map getParameters(List<String> keys) {
305 if (keys == null) {
306 throw new IllegalArgumentException("The argument must not be a null pointer");
307 }
308 Map<String, String> results;
309 synchronized (mLock) {
310 checkNotClosedLocked();
311 results = nativeGetParameters(mNativeContext, keys);
312 }
313 if (results == null) return Collections.emptyMap();
314 return results;
315 }
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -0700316}