blob: 8f3f099c96016206fbc04eaff6450c47f7165e46 [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 {
Tomasz Wasilczykd0ac0bc2018-03-01 08:21:55 -080093 Slog.e(TAG, "Failed to notify AudioService about new state: "
94 + connected + ", response was: " + ret);
Tomasz Wasilczykca98cde2018-01-04 12:26:40 -080095 }
96 }
97
Tomasz Wasilczykf58305d2017-12-28 14:03:15 -080098 @Override
Tomasz Wasilczykca98cde2018-01-04 12:26:40 -080099 public void setConfiguration(RadioManager.BandConfig config) {
100 synchronized (mLock) {
101 checkNotClosedLocked();
102 mDummyConfig = Objects.requireNonNull(config);
103 Slog.i(TAG, "Ignoring setConfiguration - not applicable for broadcastradio HAL 2.x");
104 TunerCallback.dispatch(() -> mCallback.mClientCb.onConfigurationChanged(config));
105 }
106 }
Tomasz Wasilczykf58305d2017-12-28 14:03:15 -0800107
108 @Override
109 public RadioManager.BandConfig getConfiguration() {
Tomasz Wasilczykca98cde2018-01-04 12:26:40 -0800110 synchronized (mLock) {
111 checkNotClosedLocked();
112 return mDummyConfig;
113 }
Tomasz Wasilczykf58305d2017-12-28 14:03:15 -0800114 }
115
116 @Override
Tomasz Wasilczykca98cde2018-01-04 12:26:40 -0800117 public void setMuted(boolean mute) {
118 synchronized (mLock) {
119 checkNotClosedLocked();
120 if (mIsMuted == mute) return;
121 mIsMuted = mute;
122 notifyAudioServiceLocked(!mute);
123 }
124 }
Tomasz Wasilczykf58305d2017-12-28 14:03:15 -0800125
126 @Override
127 public boolean isMuted() {
Tomasz Wasilczykca98cde2018-01-04 12:26:40 -0800128 synchronized (mLock) {
129 checkNotClosedLocked();
130 return mIsMuted;
131 }
Tomasz Wasilczykf58305d2017-12-28 14:03:15 -0800132 }
133
134 @Override
Tomasz Wasilczyk3b4465e2018-01-14 21:47:44 -0800135 public void step(boolean directionDown, boolean skipSubChannel) throws RemoteException {
Tomasz Wasilczykca98cde2018-01-04 12:26:40 -0800136 synchronized (mLock) {
137 checkNotClosedLocked();
Tomasz Wasilczyk3b4465e2018-01-14 21:47:44 -0800138 int halResult = mHwSession.step(!directionDown);
139 Convert.throwOnError("step", halResult);
Tomasz Wasilczykca98cde2018-01-04 12:26:40 -0800140 }
141 }
Tomasz Wasilczykf58305d2017-12-28 14:03:15 -0800142
143 @Override
Tomasz Wasilczyk3b4465e2018-01-14 21:47:44 -0800144 public void scan(boolean directionDown, boolean skipSubChannel) throws RemoteException {
Tomasz Wasilczykca98cde2018-01-04 12:26:40 -0800145 synchronized (mLock) {
146 checkNotClosedLocked();
Tomasz Wasilczyk3b4465e2018-01-14 21:47:44 -0800147 int halResult = mHwSession.scan(!directionDown, skipSubChannel);
148 Convert.throwOnError("step", halResult);
Tomasz Wasilczykca98cde2018-01-04 12:26:40 -0800149 }
150 }
Tomasz Wasilczykf58305d2017-12-28 14:03:15 -0800151
152 @Override
Tomasz Wasilczyk3b4465e2018-01-14 21:47:44 -0800153 public void tune(ProgramSelector selector) throws RemoteException {
Tomasz Wasilczykca98cde2018-01-04 12:26:40 -0800154 synchronized (mLock) {
155 checkNotClosedLocked();
Tomasz Wasilczyk3b4465e2018-01-14 21:47:44 -0800156 int halResult = mHwSession.tune(Convert.programSelectorToHal(selector));
157 Convert.throwOnError("tune", halResult);
Tomasz Wasilczykca98cde2018-01-04 12:26:40 -0800158 }
159 }
Tomasz Wasilczykf58305d2017-12-28 14:03:15 -0800160
161 @Override
Tomasz Wasilczykca98cde2018-01-04 12:26:40 -0800162 public void cancel() {
163 synchronized (mLock) {
164 checkNotClosedLocked();
Tomasz Wasilczyk3b4465e2018-01-14 21:47:44 -0800165 Utils.maybeRethrow(mHwSession::cancel);
Tomasz Wasilczykca98cde2018-01-04 12:26:40 -0800166 }
167 }
Tomasz Wasilczykf58305d2017-12-28 14:03:15 -0800168
169 @Override
Tomasz Wasilczykca98cde2018-01-04 12:26:40 -0800170 public void cancelAnnouncement() {
Tomasz Wasilczyk3b4465e2018-01-14 21:47:44 -0800171 Slog.i(TAG, "Announcements control doesn't involve cancelling at the HAL level in 2.x");
Tomasz Wasilczykf58305d2017-12-28 14:03:15 -0800172 }
173
174 @Override
175 public Bitmap getImage(int id) {
Tomasz Wasilczyk3b4465e2018-01-14 21:47:44 -0800176 return mModule.getImage(id);
Tomasz Wasilczykf58305d2017-12-28 14:03:15 -0800177 }
178
179 @Override
180 public boolean startBackgroundScan() {
Tomasz Wasilczykca98cde2018-01-04 12:26:40 -0800181 Slog.i(TAG, "Explicit background scan trigger is not supported with HAL 2.x");
Tomasz Wasilczyk3b4465e2018-01-14 21:47:44 -0800182 TunerCallback.dispatch(() -> mCallback.mClientCb.onBackgroundScanComplete());
183 return true;
Tomasz Wasilczykf58305d2017-12-28 14:03:15 -0800184 }
185
186 @Override
Tomasz Wasilczyk436128f2018-01-08 16:46:09 -0800187 public void startProgramListUpdates(ProgramList.Filter filter) throws RemoteException {
Tomasz Wasilczykca98cde2018-01-04 12:26:40 -0800188 synchronized (mLock) {
189 checkNotClosedLocked();
Tomasz Wasilczyk436128f2018-01-08 16:46:09 -0800190 int halResult = mHwSession.startProgramListUpdates(Convert.programFilterToHal(filter));
191 Convert.throwOnError("startProgramListUpdates", halResult);
192 }
193 }
194
195 @Override
196 public void stopProgramListUpdates() throws RemoteException {
197 synchronized (mLock) {
198 checkNotClosedLocked();
199 mHwSession.stopProgramListUpdates();
Tomasz Wasilczykca98cde2018-01-04 12:26:40 -0800200 }
201 }
202
Tomasz Wasilczykce40fe92018-01-04 20:52:39 -0800203 @Override
204 public boolean isConfigFlagSupported(int flag) {
205 try {
206 isConfigFlagSet(flag);
207 return true;
208 } catch (IllegalStateException ex) {
209 return true;
210 } catch (UnsupportedOperationException ex) {
211 return false;
212 }
213 }
214
215 @Override
216 public boolean isConfigFlagSet(int flag) {
217 Slog.v(TAG, "isConfigFlagSet " + ConfigFlag.toString(flag));
Tomasz Wasilczykca98cde2018-01-04 12:26:40 -0800218 synchronized (mLock) {
219 checkNotClosedLocked();
220
221 MutableInt halResult = new MutableInt(Result.UNKNOWN_ERROR);
222 MutableBoolean flagState = new MutableBoolean(false);
223 try {
Tomasz Wasilczyk3097cbf2018-01-12 14:59:13 -0800224 mHwSession.isConfigFlagSet(flag, (int result, boolean value) -> {
Tomasz Wasilczykca98cde2018-01-04 12:26:40 -0800225 halResult.value = result;
226 flagState.value = value;
227 });
228 } catch (RemoteException ex) {
Tomasz Wasilczykce40fe92018-01-04 20:52:39 -0800229 throw new RuntimeException("Failed to check flag " + ConfigFlag.toString(flag), ex);
Tomasz Wasilczykca98cde2018-01-04 12:26:40 -0800230 }
Tomasz Wasilczykce40fe92018-01-04 20:52:39 -0800231 Convert.throwOnError("isConfigFlagSet", halResult.value);
Tomasz Wasilczykca98cde2018-01-04 12:26:40 -0800232
233 return flagState.value;
234 }
235 }
236
Tomasz Wasilczykce40fe92018-01-04 20:52:39 -0800237 @Override
Tomasz Wasilczyk436128f2018-01-08 16:46:09 -0800238 public void setConfigFlag(int flag, boolean value) throws RemoteException {
Tomasz Wasilczykca98cde2018-01-04 12:26:40 -0800239 Slog.v(TAG, "setConfigFlag " + ConfigFlag.toString(flag) + " = " + value);
240 synchronized (mLock) {
241 checkNotClosedLocked();
Tomasz Wasilczyk436128f2018-01-08 16:46:09 -0800242 int halResult = mHwSession.setConfigFlag(flag, value);
Tomasz Wasilczykca98cde2018-01-04 12:26:40 -0800243 Convert.throwOnError("setConfigFlag", halResult);
244 }
Tomasz Wasilczykf58305d2017-12-28 14:03:15 -0800245 }
246
247 @Override
Tomasz Wasilczykf58305d2017-12-28 14:03:15 -0800248 public Map setParameters(Map parameters) {
Tomasz Wasilczykca98cde2018-01-04 12:26:40 -0800249 synchronized (mLock) {
250 checkNotClosedLocked();
Tomasz Wasilczyk3b4465e2018-01-14 21:47:44 -0800251 return Convert.vendorInfoFromHal(Utils.maybeRethrow(
252 () -> mHwSession.setParameters(Convert.vendorInfoToHal(parameters))));
Tomasz Wasilczykca98cde2018-01-04 12:26:40 -0800253 }
Tomasz Wasilczykf58305d2017-12-28 14:03:15 -0800254 }
255
256 @Override
257 public Map getParameters(List<String> keys) {
Tomasz Wasilczykca98cde2018-01-04 12:26:40 -0800258 synchronized (mLock) {
259 checkNotClosedLocked();
Tomasz Wasilczyk3b4465e2018-01-14 21:47:44 -0800260 return Convert.vendorInfoFromHal(Utils.maybeRethrow(
261 () -> mHwSession.getParameters(Convert.listToArrayList(keys))));
Tomasz Wasilczykca98cde2018-01-04 12:26:40 -0800262 }
Tomasz Wasilczykf58305d2017-12-28 14:03:15 -0800263 }
264}