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.hal1; 18 19 import android.annotation.NonNull; 20 import android.graphics.Bitmap; 21 import android.graphics.BitmapFactory; 22 import android.hardware.radio.ITuner; 23 import android.hardware.radio.ITunerCallback; 24 import android.hardware.radio.ProgramList; 25 import android.hardware.radio.ProgramSelector; 26 import android.hardware.radio.RadioManager; 27 import android.os.IBinder; 28 import android.os.RemoteException; 29 30 import com.android.server.broadcastradio.RadioServiceUserController; 31 import com.android.server.utils.Slogf; 32 33 import java.util.List; 34 import java.util.Map; 35 36 class Tuner extends ITuner.Stub { 37 38 private static final String TAG = "BcRadio1Srv.Tuner"; 39 40 /** 41 * This field is used by native code, do not access or modify. 42 */ 43 private final long mNativeContext; 44 45 private final Object mLock = new Object(); 46 private final TunerCallback mTunerCallback; 47 private final ITunerCallback mClientCallback; 48 private final IBinder.DeathRecipient mDeathRecipient; 49 50 private boolean mIsClosed = false; 51 private boolean mIsMuted = false; 52 private int mRegion; 53 private final boolean mWithAudio; 54 Tuner(@onNull ITunerCallback clientCallback, int halRev, int region, boolean withAudio, int band)55 Tuner(@NonNull ITunerCallback clientCallback, int halRev, 56 int region, boolean withAudio, int band) { 57 mClientCallback = clientCallback; 58 mTunerCallback = new TunerCallback(this, clientCallback, halRev); 59 mRegion = region; 60 mWithAudio = withAudio; 61 mNativeContext = nativeInit(halRev, withAudio, band); 62 mDeathRecipient = this::close; 63 try { 64 mClientCallback.asBinder().linkToDeath(mDeathRecipient, 0); 65 } catch (RemoteException ex) { 66 close(); 67 } 68 } 69 70 @Override finalize()71 protected void finalize() throws Throwable { 72 nativeFinalize(mNativeContext); 73 super.finalize(); 74 } 75 nativeInit(int halRev, boolean withAudio, int band)76 private native long nativeInit(int halRev, boolean withAudio, int band); nativeFinalize(long nativeContext)77 private native void nativeFinalize(long nativeContext); nativeClose(long nativeContext)78 private native void nativeClose(long nativeContext); 79 nativeSetConfiguration(long nativeContext, @NonNull RadioManager.BandConfig config)80 private native void nativeSetConfiguration(long nativeContext, 81 @NonNull RadioManager.BandConfig config); nativeGetConfiguration(long nativeContext, int region)82 private native RadioManager.BandConfig nativeGetConfiguration(long nativeContext, int region); 83 nativeStep(long nativeContext, boolean directionDown, boolean skipSubChannel)84 private native void nativeStep(long nativeContext, boolean directionDown, boolean skipSubChannel); nativeScan(long nativeContext, boolean directionDown, boolean skipSubChannel)85 private native void nativeScan(long nativeContext, boolean directionDown, boolean skipSubChannel); nativeTune(long nativeContext, @NonNull ProgramSelector selector)86 private native void nativeTune(long nativeContext, @NonNull ProgramSelector selector); nativeCancel(long nativeContext)87 private native void nativeCancel(long nativeContext); 88 nativeCancelAnnouncement(long nativeContext)89 private native void nativeCancelAnnouncement(long nativeContext); 90 nativeStartBackgroundScan(long nativeContext)91 private native boolean nativeStartBackgroundScan(long nativeContext); nativeGetProgramList(long nativeContext, Map<String, String> vendorFilter)92 private native List<RadioManager.ProgramInfo> nativeGetProgramList(long nativeContext, 93 Map<String, String> vendorFilter); 94 nativeGetImage(long nativeContext, int id)95 private native byte[] nativeGetImage(long nativeContext, int id); 96 nativeIsAnalogForced(long nativeContext)97 private native boolean nativeIsAnalogForced(long nativeContext); nativeSetAnalogForced(long nativeContext, boolean isForced)98 private native void nativeSetAnalogForced(long nativeContext, boolean isForced); 99 100 @Override close()101 public void close() { 102 synchronized (mLock) { 103 if (mIsClosed) return; 104 mIsClosed = true; 105 mTunerCallback.detach(); 106 mClientCallback.asBinder().unlinkToDeath(mDeathRecipient, 0); 107 nativeClose(mNativeContext); 108 } 109 } 110 111 @Override isClosed()112 public boolean isClosed() { 113 return mIsClosed; 114 } 115 checkNotClosedLocked()116 private void checkNotClosedLocked() { 117 if (mIsClosed) { 118 throw new IllegalStateException("Tuner is closed, no further operations are allowed"); 119 } 120 } 121 checkConfiguredLocked()122 private boolean checkConfiguredLocked() { 123 if (mTunerCallback.isInitialConfigurationDone()) return true; 124 Slogf.w(TAG, "Initial configuration is still pending, skipping the operation"); 125 return false; 126 } 127 128 @Override setConfiguration(RadioManager.BandConfig config)129 public void setConfiguration(RadioManager.BandConfig config) { 130 if (!RadioServiceUserController.isCurrentOrSystemUser()) { 131 Slogf.w(TAG, "Cannot set configuration for HAL 1.x client from non-current user"); 132 return; 133 } 134 if (config == null) { 135 throw new IllegalArgumentException("The argument must not be a null pointer"); 136 } 137 synchronized (mLock) { 138 checkNotClosedLocked(); 139 nativeSetConfiguration(mNativeContext, config); 140 mRegion = config.getRegion(); 141 } 142 } 143 144 @Override getConfiguration()145 public RadioManager.BandConfig getConfiguration() { 146 synchronized (mLock) { 147 checkNotClosedLocked(); 148 return nativeGetConfiguration(mNativeContext, mRegion); 149 } 150 } 151 152 @Override setMuted(boolean mute)153 public void setMuted(boolean mute) { 154 if (!mWithAudio) { 155 throw new IllegalStateException("Can't operate on mute - no audio requested"); 156 } 157 synchronized (mLock) { 158 checkNotClosedLocked(); 159 if (mIsMuted == mute) return; 160 mIsMuted = mute; 161 Slogf.w(TAG, "Mute via RadioService is not implemented - please handle it via app"); 162 } 163 } 164 165 @Override isMuted()166 public boolean isMuted() { 167 if (!mWithAudio) { 168 Slogf.w(TAG, "Tuner did not request audio, pretending it was muted"); 169 return true; 170 } 171 synchronized (mLock) { 172 checkNotClosedLocked(); 173 return mIsMuted; 174 } 175 } 176 177 @Override step(boolean directionDown, boolean skipSubChannel)178 public void step(boolean directionDown, boolean skipSubChannel) { 179 if (!RadioServiceUserController.isCurrentOrSystemUser()) { 180 Slogf.w(TAG, "Cannot step on HAL 1.x client from non-current user"); 181 return; 182 } 183 synchronized (mLock) { 184 checkNotClosedLocked(); 185 if (!checkConfiguredLocked()) return; 186 nativeStep(mNativeContext, directionDown, skipSubChannel); 187 } 188 } 189 190 @Override seek(boolean directionDown, boolean skipSubChannel)191 public void seek(boolean directionDown, boolean skipSubChannel) { 192 if (!RadioServiceUserController.isCurrentOrSystemUser()) { 193 Slogf.w(TAG, "Cannot seek on HAL 1.x client from non-current user"); 194 return; 195 } 196 synchronized (mLock) { 197 checkNotClosedLocked(); 198 if (!checkConfiguredLocked()) return; 199 nativeScan(mNativeContext, directionDown, skipSubChannel); 200 } 201 } 202 203 @Override tune(ProgramSelector selector)204 public void tune(ProgramSelector selector) { 205 if (!RadioServiceUserController.isCurrentOrSystemUser()) { 206 Slogf.w(TAG, "Cannot tune on HAL 1.x client from non-current user"); 207 return; 208 } 209 if (selector == null) { 210 throw new IllegalArgumentException("The argument must not be a null pointer"); 211 } 212 Slogf.i(TAG, "Tuning to " + selector); 213 synchronized (mLock) { 214 checkNotClosedLocked(); 215 if (!checkConfiguredLocked()) return; 216 nativeTune(mNativeContext, selector); 217 } 218 } 219 220 @Override cancel()221 public void cancel() { 222 if (!RadioServiceUserController.isCurrentOrSystemUser()) { 223 Slogf.w(TAG, "Cannot cancel on HAL 1.x client from non-current user"); 224 return; 225 } 226 synchronized (mLock) { 227 checkNotClosedLocked(); 228 nativeCancel(mNativeContext); 229 } 230 } 231 232 @Override cancelAnnouncement()233 public void cancelAnnouncement() { 234 if (!RadioServiceUserController.isCurrentOrSystemUser()) { 235 Slogf.w(TAG, "Cannot cancel announcement on HAL 1.x client from non-current user"); 236 return; 237 } 238 synchronized (mLock) { 239 checkNotClosedLocked(); 240 nativeCancelAnnouncement(mNativeContext); 241 } 242 } 243 244 @Override getImage(int id)245 public Bitmap getImage(int id) { 246 if (id == 0) { 247 throw new IllegalArgumentException("Image ID is missing"); 248 } 249 250 byte[] rawImage; 251 synchronized (mLock) { 252 rawImage = nativeGetImage(mNativeContext, id); 253 } 254 if (rawImage == null || rawImage.length == 0) { 255 return null; 256 } 257 258 return BitmapFactory.decodeByteArray(rawImage, 0, rawImage.length); 259 } 260 261 @Override startBackgroundScan()262 public boolean startBackgroundScan() { 263 if (!RadioServiceUserController.isCurrentOrSystemUser()) { 264 Slogf.w(TAG, 265 "Cannot start background scan on HAL 1.x client from non-current user"); 266 return false; 267 } 268 synchronized (mLock) { 269 checkNotClosedLocked(); 270 return nativeStartBackgroundScan(mNativeContext); 271 } 272 } 273 getProgramList(Map vendorFilter)274 List<RadioManager.ProgramInfo> getProgramList(Map vendorFilter) { 275 Map<String, String> sFilter = vendorFilter; 276 synchronized (mLock) { 277 checkNotClosedLocked(); 278 List<RadioManager.ProgramInfo> list = nativeGetProgramList(mNativeContext, sFilter); 279 if (list == null) { 280 throw new IllegalStateException("Program list is not ready"); 281 } 282 return list; 283 } 284 } 285 286 @Override startProgramListUpdates(ProgramList.Filter filter)287 public void startProgramListUpdates(ProgramList.Filter filter) { 288 if (!RadioServiceUserController.isCurrentOrSystemUser()) { 289 Slogf.w(TAG, 290 "Cannot start program list updates on HAL 1.x client from non-current user"); 291 return; 292 } 293 mTunerCallback.startProgramListUpdates(filter); 294 } 295 296 @Override stopProgramListUpdates()297 public void stopProgramListUpdates() { 298 if (!RadioServiceUserController.isCurrentOrSystemUser()) { 299 Slogf.w(TAG, 300 "Cannot stop program list updates on HAL 1.x client from non-current user"); 301 return; 302 } 303 mTunerCallback.stopProgramListUpdates(); 304 } 305 306 @Override isConfigFlagSupported(int flag)307 public boolean isConfigFlagSupported(int flag) { 308 return flag == RadioManager.CONFIG_FORCE_ANALOG; 309 } 310 311 @Override isConfigFlagSet(int flag)312 public boolean isConfigFlagSet(int flag) { 313 if (flag == RadioManager.CONFIG_FORCE_ANALOG) { 314 synchronized (mLock) { 315 checkNotClosedLocked(); 316 return nativeIsAnalogForced(mNativeContext); 317 } 318 } 319 throw new UnsupportedOperationException("Not supported by HAL 1.x"); 320 } 321 322 @Override setConfigFlag(int flag, boolean value)323 public void setConfigFlag(int flag, boolean value) { 324 if (!RadioServiceUserController.isCurrentOrSystemUser()) { 325 Slogf.w(TAG, "Cannot set config flag for HAL 1.x client from non-current user"); 326 return; 327 } 328 if (flag == RadioManager.CONFIG_FORCE_ANALOG) { 329 synchronized (mLock) { 330 checkNotClosedLocked(); 331 nativeSetAnalogForced(mNativeContext, value); 332 return; 333 } 334 } 335 throw new UnsupportedOperationException("Not supported by HAL 1.x"); 336 } 337 338 @Override setParameters(Map<String, String> parameters)339 public Map<String, String> setParameters(Map<String, String> parameters) { 340 throw new UnsupportedOperationException("Not supported by HAL 1.x"); 341 } 342 343 @Override getParameters(List<String> keys)344 public Map<String, String> getParameters(List<String> keys) { 345 throw new UnsupportedOperationException("Not supported by HAL 1.x"); 346 } 347 } 348