1 /** 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 17 package com.android.server.broadcastradio.hal2; 18 19 import android.annotation.NonNull; 20 import android.graphics.Bitmap; 21 import android.hardware.broadcastradio.V2_0.ConfigFlag; 22 import android.hardware.broadcastradio.V2_0.ITunerSession; 23 import android.hardware.broadcastradio.V2_0.Result; 24 import android.hardware.radio.ITuner; 25 import android.hardware.radio.ProgramList; 26 import android.hardware.radio.ProgramSelector; 27 import android.hardware.radio.RadioManager; 28 import android.os.RemoteException; 29 import android.util.MutableBoolean; 30 import android.util.MutableInt; 31 import android.util.Slog; 32 33 import java.util.List; 34 import java.util.Map; 35 import java.util.Objects; 36 37 class TunerSession extends ITuner.Stub { 38 private static final String TAG = "BcRadio2Srv.session"; 39 private static final String kAudioDeviceName = "Radio tuner source"; 40 41 private final Object mLock = new Object(); 42 43 private final RadioModule mModule; 44 private final ITunerSession mHwSession; 45 private final TunerCallback mCallback; 46 private boolean mIsClosed = false; 47 private boolean mIsMuted = false; 48 49 // necessary only for older APIs compatibility 50 private RadioManager.BandConfig mDummyConfig = null; 51 52 TunerSession(@NonNull RadioModule module, @NonNull ITunerSession hwSession, 53 @NonNull TunerCallback callback) { 54 mModule = Objects.requireNonNull(module); 55 mHwSession = Objects.requireNonNull(hwSession); 56 mCallback = Objects.requireNonNull(callback); 57 } 58 59 @Override 60 public void close() { 61 synchronized (mLock) { 62 if (mIsClosed) return; 63 mIsClosed = true; 64 } 65 } 66 67 @Override 68 public boolean isClosed() { 69 return mIsClosed; 70 } 71 72 private void checkNotClosedLocked() { 73 if (mIsClosed) { 74 throw new IllegalStateException("Tuner is closed, no further operations are allowed"); 75 } 76 } 77 78 @Override 79 public void setConfiguration(RadioManager.BandConfig config) { 80 synchronized (mLock) { 81 checkNotClosedLocked(); 82 mDummyConfig = Objects.requireNonNull(config); 83 Slog.i(TAG, "Ignoring setConfiguration - not applicable for broadcastradio HAL 2.x"); 84 TunerCallback.dispatch(() -> mCallback.mClientCb.onConfigurationChanged(config)); 85 } 86 } 87 88 @Override 89 public RadioManager.BandConfig getConfiguration() { 90 synchronized (mLock) { 91 checkNotClosedLocked(); 92 return mDummyConfig; 93 } 94 } 95 96 @Override 97 public void setMuted(boolean mute) { 98 synchronized (mLock) { 99 checkNotClosedLocked(); 100 if (mIsMuted == mute) return; 101 mIsMuted = mute; 102 Slog.w(TAG, "Mute via RadioService is not implemented - please handle it via app"); 103 } 104 } 105 106 @Override 107 public boolean isMuted() { 108 synchronized (mLock) { 109 checkNotClosedLocked(); 110 return mIsMuted; 111 } 112 } 113 114 @Override 115 public void step(boolean directionDown, boolean skipSubChannel) throws RemoteException { 116 synchronized (mLock) { 117 checkNotClosedLocked(); 118 int halResult = mHwSession.step(!directionDown); 119 Convert.throwOnError("step", halResult); 120 } 121 } 122 123 @Override 124 public void scan(boolean directionDown, boolean skipSubChannel) throws RemoteException { 125 synchronized (mLock) { 126 checkNotClosedLocked(); 127 int halResult = mHwSession.scan(!directionDown, skipSubChannel); 128 Convert.throwOnError("step", halResult); 129 } 130 } 131 132 @Override 133 public void tune(ProgramSelector selector) throws RemoteException { 134 synchronized (mLock) { 135 checkNotClosedLocked(); 136 int halResult = mHwSession.tune(Convert.programSelectorToHal(selector)); 137 Convert.throwOnError("tune", halResult); 138 } 139 } 140 141 @Override 142 public void cancel() { 143 synchronized (mLock) { 144 checkNotClosedLocked(); 145 Utils.maybeRethrow(mHwSession::cancel); 146 } 147 } 148 149 @Override 150 public void cancelAnnouncement() { 151 Slog.i(TAG, "Announcements control doesn't involve cancelling at the HAL level in 2.x"); 152 } 153 154 @Override 155 public Bitmap getImage(int id) { 156 return mModule.getImage(id); 157 } 158 159 @Override 160 public boolean startBackgroundScan() { 161 Slog.i(TAG, "Explicit background scan trigger is not supported with HAL 2.x"); 162 TunerCallback.dispatch(() -> mCallback.mClientCb.onBackgroundScanComplete()); 163 return true; 164 } 165 166 @Override 167 public void startProgramListUpdates(ProgramList.Filter filter) throws RemoteException { 168 synchronized (mLock) { 169 checkNotClosedLocked(); 170 int halResult = mHwSession.startProgramListUpdates(Convert.programFilterToHal(filter)); 171 Convert.throwOnError("startProgramListUpdates", halResult); 172 } 173 } 174 175 @Override 176 public void stopProgramListUpdates() throws RemoteException { 177 synchronized (mLock) { 178 checkNotClosedLocked(); 179 mHwSession.stopProgramListUpdates(); 180 } 181 } 182 183 @Override 184 public boolean isConfigFlagSupported(int flag) { 185 try { 186 isConfigFlagSet(flag); 187 return true; 188 } catch (IllegalStateException ex) { 189 return true; 190 } catch (UnsupportedOperationException ex) { 191 return false; 192 } 193 } 194 195 @Override 196 public boolean isConfigFlagSet(int flag) { 197 Slog.v(TAG, "isConfigFlagSet " + ConfigFlag.toString(flag)); 198 synchronized (mLock) { 199 checkNotClosedLocked(); 200 201 MutableInt halResult = new MutableInt(Result.UNKNOWN_ERROR); 202 MutableBoolean flagState = new MutableBoolean(false); 203 try { 204 mHwSession.isConfigFlagSet(flag, (int result, boolean value) -> { 205 halResult.value = result; 206 flagState.value = value; 207 }); 208 } catch (RemoteException ex) { 209 throw new RuntimeException("Failed to check flag " + ConfigFlag.toString(flag), ex); 210 } 211 Convert.throwOnError("isConfigFlagSet", halResult.value); 212 213 return flagState.value; 214 } 215 } 216 217 @Override 218 public void setConfigFlag(int flag, boolean value) throws RemoteException { 219 Slog.v(TAG, "setConfigFlag " + ConfigFlag.toString(flag) + " = " + value); 220 synchronized (mLock) { 221 checkNotClosedLocked(); 222 int halResult = mHwSession.setConfigFlag(flag, value); 223 Convert.throwOnError("setConfigFlag", halResult); 224 } 225 } 226 227 @Override 228 public Map setParameters(Map parameters) { 229 synchronized (mLock) { 230 checkNotClosedLocked(); 231 return Convert.vendorInfoFromHal(Utils.maybeRethrow( 232 () -> mHwSession.setParameters(Convert.vendorInfoToHal(parameters)))); 233 } 234 } 235 236 @Override 237 public Map getParameters(List<String> keys) { 238 synchronized (mLock) { 239 checkNotClosedLocked(); 240 return Convert.vendorInfoFromHal(Utils.maybeRethrow( 241 () -> mHwSession.getParameters(Convert.listToArrayList(keys)))); 242 } 243 } 244 } 245