blob: 8efaa2a5357225a5e0a5018d7e918c84348b7efe [file] [log] [blame]
Tomasz Wasilczykf58305d2017-12-28 14:03:15 -08001/**
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
17package com.android.server.broadcastradio.hal2;
18
19import android.annotation.NonNull;
20import android.graphics.Bitmap;
Tomasz Wasilczykca98cde2018-01-04 12:26:40 -080021import android.hardware.broadcastradio.V2_0.ConfigFlag;
Tomasz Wasilczykf58305d2017-12-28 14:03:15 -080022import android.hardware.broadcastradio.V2_0.ITunerSession;
Tomasz Wasilczykca98cde2018-01-04 12:26:40 -080023import android.hardware.broadcastradio.V2_0.Result;
Tomasz Wasilczykf58305d2017-12-28 14:03:15 -080024import android.hardware.radio.ITuner;
Tomasz Wasilczyk436128f2018-01-08 16:46:09 -080025import android.hardware.radio.ProgramList;
Tomasz Wasilczykf58305d2017-12-28 14:03:15 -080026import android.hardware.radio.ProgramSelector;
27import android.hardware.radio.RadioManager;
Tomasz Wasilczykca98cde2018-01-04 12:26:40 -080028import android.media.AudioSystem;
29import android.os.RemoteException;
30import android.util.MutableBoolean;
31import android.util.MutableInt;
Tomasz Wasilczykf58305d2017-12-28 14:03:15 -080032import android.util.Slog;
33
34import java.util.List;
35import java.util.Map;
36import java.util.Objects;
37
38class TunerSession extends ITuner.Stub {
39 private static final String TAG = "BcRadio2Srv.session";
Tomasz Wasilczykca98cde2018-01-04 12:26:40 -080040 private static final String kAudioDeviceName = "Radio tuner source";
Tomasz Wasilczykf58305d2017-12-28 14:03:15 -080041
42 private final Object mLock = new Object();
43
Tomasz Wasilczyk3b4465e2018-01-14 21:47:44 -080044 private final RadioModule mModule;
Tomasz Wasilczykf58305d2017-12-28 14:03:15 -080045 private final ITunerSession mHwSession;
46 private final TunerCallback mCallback;
47 private boolean mIsClosed = false;
Tomasz Wasilczykca98cde2018-01-04 12:26:40 -080048 private boolean mIsAudioConnected = false;
49 private boolean mIsMuted = false;
50
51 // necessary only for older APIs compatibility
52 private RadioManager.BandConfig mDummyConfig = null;
Tomasz Wasilczykf58305d2017-12-28 14:03:15 -080053
Tomasz Wasilczyk3b4465e2018-01-14 21:47:44 -080054 TunerSession(@NonNull RadioModule module, @NonNull ITunerSession hwSession,
55 @NonNull TunerCallback callback) {
56 mModule = Objects.requireNonNull(module);
Tomasz Wasilczykf58305d2017-12-28 14:03:15 -080057 mHwSession = Objects.requireNonNull(hwSession);
58 mCallback = Objects.requireNonNull(callback);
Tomasz Wasilczykca98cde2018-01-04 12:26:40 -080059 notifyAudioServiceLocked(true);
Tomasz Wasilczykf58305d2017-12-28 14:03:15 -080060 }
61
62 @Override
63 public void close() {
64 synchronized (mLock) {
65 if (mIsClosed) return;
66 mIsClosed = true;
Tomasz Wasilczykca98cde2018-01-04 12:26:40 -080067 notifyAudioServiceLocked(false);
Tomasz Wasilczykf58305d2017-12-28 14:03:15 -080068 }
69 }
70
71 @Override
72 public boolean isClosed() {
73 return mIsClosed;
74 }
75
76 private void checkNotClosedLocked() {
77 if (mIsClosed) {
78 throw new IllegalStateException("Tuner is closed, no further operations are allowed");
79 }
80 }
81
Tomasz Wasilczykca98cde2018-01-04 12:26:40 -080082 private void notifyAudioServiceLocked(boolean connected) {
83 if (mIsAudioConnected == connected) return;
84
85 Slog.d(TAG, "Notifying AudioService about new state: " + connected);
86 int ret = AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_IN_FM_TUNER,
87 connected ? AudioSystem.DEVICE_STATE_AVAILABLE : AudioSystem.DEVICE_STATE_UNAVAILABLE,
88 null, kAudioDeviceName);
89
90 if (ret == AudioSystem.AUDIO_STATUS_OK) {
91 mIsAudioConnected = connected;
92 } else {
93 Slog.e(TAG, "Failed to notify AudioService about new state: " + connected);
94 }
95 }
96
Tomasz Wasilczykf58305d2017-12-28 14:03:15 -080097 @Override
Tomasz Wasilczykca98cde2018-01-04 12:26:40 -080098 public void setConfiguration(RadioManager.BandConfig config) {
99 synchronized (mLock) {
100 checkNotClosedLocked();
101 mDummyConfig = Objects.requireNonNull(config);
102 Slog.i(TAG, "Ignoring setConfiguration - not applicable for broadcastradio HAL 2.x");
103 TunerCallback.dispatch(() -> mCallback.mClientCb.onConfigurationChanged(config));
104 }
105 }
Tomasz Wasilczykf58305d2017-12-28 14:03:15 -0800106
107 @Override
108 public RadioManager.BandConfig getConfiguration() {
Tomasz Wasilczykca98cde2018-01-04 12:26:40 -0800109 synchronized (mLock) {
110 checkNotClosedLocked();
111 return mDummyConfig;
112 }
Tomasz Wasilczykf58305d2017-12-28 14:03:15 -0800113 }
114
115 @Override
Tomasz Wasilczykca98cde2018-01-04 12:26:40 -0800116 public void setMuted(boolean mute) {
117 synchronized (mLock) {
118 checkNotClosedLocked();
119 if (mIsMuted == mute) return;
120 mIsMuted = mute;
121 notifyAudioServiceLocked(!mute);
122 }
123 }
Tomasz Wasilczykf58305d2017-12-28 14:03:15 -0800124
125 @Override
126 public boolean isMuted() {
Tomasz Wasilczykca98cde2018-01-04 12:26:40 -0800127 synchronized (mLock) {
128 checkNotClosedLocked();
129 return mIsMuted;
130 }
Tomasz Wasilczykf58305d2017-12-28 14:03:15 -0800131 }
132
133 @Override
Tomasz Wasilczyk3b4465e2018-01-14 21:47:44 -0800134 public void step(boolean directionDown, boolean skipSubChannel) throws RemoteException {
Tomasz Wasilczykca98cde2018-01-04 12:26:40 -0800135 synchronized (mLock) {
136 checkNotClosedLocked();
Tomasz Wasilczyk3b4465e2018-01-14 21:47:44 -0800137 int halResult = mHwSession.step(!directionDown);
138 Convert.throwOnError("step", halResult);
Tomasz Wasilczykca98cde2018-01-04 12:26:40 -0800139 }
140 }
Tomasz Wasilczykf58305d2017-12-28 14:03:15 -0800141
142 @Override
Tomasz Wasilczyk3b4465e2018-01-14 21:47:44 -0800143 public void scan(boolean directionDown, boolean skipSubChannel) throws RemoteException {
Tomasz Wasilczykca98cde2018-01-04 12:26:40 -0800144 synchronized (mLock) {
145 checkNotClosedLocked();
Tomasz Wasilczyk3b4465e2018-01-14 21:47:44 -0800146 int halResult = mHwSession.scan(!directionDown, skipSubChannel);
147 Convert.throwOnError("step", halResult);
Tomasz Wasilczykca98cde2018-01-04 12:26:40 -0800148 }
149 }
Tomasz Wasilczykf58305d2017-12-28 14:03:15 -0800150
151 @Override
Tomasz Wasilczyk3b4465e2018-01-14 21:47:44 -0800152 public void tune(ProgramSelector selector) throws RemoteException {
Tomasz Wasilczykca98cde2018-01-04 12:26:40 -0800153 synchronized (mLock) {
154 checkNotClosedLocked();
Tomasz Wasilczyk3b4465e2018-01-14 21:47:44 -0800155 int halResult = mHwSession.tune(Convert.programSelectorToHal(selector));
156 Convert.throwOnError("tune", halResult);
Tomasz Wasilczykca98cde2018-01-04 12:26:40 -0800157 }
158 }
Tomasz Wasilczykf58305d2017-12-28 14:03:15 -0800159
160 @Override
Tomasz Wasilczykca98cde2018-01-04 12:26:40 -0800161 public void cancel() {
162 synchronized (mLock) {
163 checkNotClosedLocked();
Tomasz Wasilczyk3b4465e2018-01-14 21:47:44 -0800164 Utils.maybeRethrow(mHwSession::cancel);
Tomasz Wasilczykca98cde2018-01-04 12:26:40 -0800165 }
166 }
Tomasz Wasilczykf58305d2017-12-28 14:03:15 -0800167
168 @Override
Tomasz Wasilczykca98cde2018-01-04 12:26:40 -0800169 public void cancelAnnouncement() {
Tomasz Wasilczyk3b4465e2018-01-14 21:47:44 -0800170 Slog.i(TAG, "Announcements control doesn't involve cancelling at the HAL level in 2.x");
Tomasz Wasilczykf58305d2017-12-28 14:03:15 -0800171 }
172
173 @Override
174 public Bitmap getImage(int id) {
Tomasz Wasilczyk3b4465e2018-01-14 21:47:44 -0800175 return mModule.getImage(id);
Tomasz Wasilczykf58305d2017-12-28 14:03:15 -0800176 }
177
178 @Override
179 public boolean startBackgroundScan() {
Tomasz Wasilczykca98cde2018-01-04 12:26:40 -0800180 Slog.i(TAG, "Explicit background scan trigger is not supported with HAL 2.x");
Tomasz Wasilczyk3b4465e2018-01-14 21:47:44 -0800181 TunerCallback.dispatch(() -> mCallback.mClientCb.onBackgroundScanComplete());
182 return true;
Tomasz Wasilczykf58305d2017-12-28 14:03:15 -0800183 }
184
185 @Override
Tomasz Wasilczyk436128f2018-01-08 16:46:09 -0800186 public void startProgramListUpdates(ProgramList.Filter filter) throws RemoteException {
Tomasz Wasilczykca98cde2018-01-04 12:26:40 -0800187 synchronized (mLock) {
188 checkNotClosedLocked();
Tomasz Wasilczyk436128f2018-01-08 16:46:09 -0800189 int halResult = mHwSession.startProgramListUpdates(Convert.programFilterToHal(filter));
190 Convert.throwOnError("startProgramListUpdates", halResult);
191 }
192 }
193
194 @Override
195 public void stopProgramListUpdates() throws RemoteException {
196 synchronized (mLock) {
197 checkNotClosedLocked();
198 mHwSession.stopProgramListUpdates();
Tomasz Wasilczykca98cde2018-01-04 12:26:40 -0800199 }
200 }
201
Tomasz Wasilczykce40fe92018-01-04 20:52:39 -0800202 @Override
203 public boolean isConfigFlagSupported(int flag) {
204 try {
205 isConfigFlagSet(flag);
206 return true;
207 } catch (IllegalStateException ex) {
208 return true;
209 } catch (UnsupportedOperationException ex) {
210 return false;
211 }
212 }
213
214 @Override
215 public boolean isConfigFlagSet(int flag) {
216 Slog.v(TAG, "isConfigFlagSet " + ConfigFlag.toString(flag));
Tomasz Wasilczykca98cde2018-01-04 12:26:40 -0800217 synchronized (mLock) {
218 checkNotClosedLocked();
219
220 MutableInt halResult = new MutableInt(Result.UNKNOWN_ERROR);
221 MutableBoolean flagState = new MutableBoolean(false);
222 try {
Tomasz Wasilczyk3097cbf2018-01-12 14:59:13 -0800223 mHwSession.isConfigFlagSet(flag, (int result, boolean value) -> {
Tomasz Wasilczykca98cde2018-01-04 12:26:40 -0800224 halResult.value = result;
225 flagState.value = value;
226 });
227 } catch (RemoteException ex) {
Tomasz Wasilczykce40fe92018-01-04 20:52:39 -0800228 throw new RuntimeException("Failed to check flag " + ConfigFlag.toString(flag), ex);
Tomasz Wasilczykca98cde2018-01-04 12:26:40 -0800229 }
Tomasz Wasilczykce40fe92018-01-04 20:52:39 -0800230 Convert.throwOnError("isConfigFlagSet", halResult.value);
Tomasz Wasilczykca98cde2018-01-04 12:26:40 -0800231
232 return flagState.value;
233 }
234 }
235
Tomasz Wasilczykce40fe92018-01-04 20:52:39 -0800236 @Override
Tomasz Wasilczyk436128f2018-01-08 16:46:09 -0800237 public void setConfigFlag(int flag, boolean value) throws RemoteException {
Tomasz Wasilczykca98cde2018-01-04 12:26:40 -0800238 Slog.v(TAG, "setConfigFlag " + ConfigFlag.toString(flag) + " = " + value);
239 synchronized (mLock) {
240 checkNotClosedLocked();
Tomasz Wasilczyk436128f2018-01-08 16:46:09 -0800241 int halResult = mHwSession.setConfigFlag(flag, value);
Tomasz Wasilczykca98cde2018-01-04 12:26:40 -0800242 Convert.throwOnError("setConfigFlag", halResult);
243 }
Tomasz Wasilczykf58305d2017-12-28 14:03:15 -0800244 }
245
246 @Override
Tomasz Wasilczykf58305d2017-12-28 14:03:15 -0800247 public Map setParameters(Map parameters) {
Tomasz Wasilczykca98cde2018-01-04 12:26:40 -0800248 synchronized (mLock) {
249 checkNotClosedLocked();
Tomasz Wasilczyk3b4465e2018-01-14 21:47:44 -0800250 return Convert.vendorInfoFromHal(Utils.maybeRethrow(
251 () -> mHwSession.setParameters(Convert.vendorInfoToHal(parameters))));
Tomasz Wasilczykca98cde2018-01-04 12:26:40 -0800252 }
Tomasz Wasilczykf58305d2017-12-28 14:03:15 -0800253 }
254
255 @Override
256 public Map getParameters(List<String> keys) {
Tomasz Wasilczykca98cde2018-01-04 12:26:40 -0800257 synchronized (mLock) {
258 checkNotClosedLocked();
Tomasz Wasilczyk3b4465e2018-01-14 21:47:44 -0800259 return Convert.vendorInfoFromHal(Utils.maybeRethrow(
260 () -> mHwSession.getParameters(Convert.listToArrayList(keys))));
Tomasz Wasilczykca98cde2018-01-04 12:26:40 -0800261 }
Tomasz Wasilczykf58305d2017-12-28 14:03:15 -0800262 }
263}