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 import android.util.Slog; 30 31 import java.util.List; 32 import java.util.Map; 33 34 class Tuner extends ITuner.Stub { 35 private static final String TAG = "BroadcastRadioService.Tuner"; 36 37 /** 38 * This field is used by native code, do not access or modify. 39 */ 40 private final long mNativeContext; 41 42 private final Object mLock = new Object(); 43 @NonNull private final TunerCallback mTunerCallback; 44 @NonNull private final ITunerCallback mClientCallback; 45 @NonNull private final IBinder.DeathRecipient mDeathRecipient; 46 47 private boolean mIsClosed = false; 48 private boolean mIsMuted = false; 49 private int mRegion; 50 private final boolean mWithAudio; 51 Tuner(@onNull ITunerCallback clientCallback, int halRev, int region, boolean withAudio, int band)52 Tuner(@NonNull ITunerCallback clientCallback, int halRev, 53 int region, boolean withAudio, int band) { 54 mClientCallback = clientCallback; 55 mTunerCallback = new TunerCallback(this, clientCallback, halRev); 56 mRegion = region; 57 mWithAudio = withAudio; 58 mNativeContext = nativeInit(halRev, withAudio, band); 59 mDeathRecipient = this::close; 60 try { 61 mClientCallback.asBinder().linkToDeath(mDeathRecipient, 0); 62 } catch (RemoteException ex) { 63 close(); 64 } 65 } 66 67 @Override finalize()68 protected void finalize() throws Throwable { 69 nativeFinalize(mNativeContext); 70 super.finalize(); 71 } 72 nativeInit(int halRev, boolean withAudio, int band)73 private native long nativeInit(int halRev, boolean withAudio, int band); nativeFinalize(long nativeContext)74 private native void nativeFinalize(long nativeContext); nativeClose(long nativeContext)75 private native void nativeClose(long nativeContext); 76 nativeSetConfiguration(long nativeContext, @NonNull RadioManager.BandConfig config)77 private native void nativeSetConfiguration(long nativeContext, 78 @NonNull RadioManager.BandConfig config); nativeGetConfiguration(long nativeContext, int region)79 private native RadioManager.BandConfig nativeGetConfiguration(long nativeContext, int region); 80 nativeStep(long nativeContext, boolean directionDown, boolean skipSubChannel)81 private native void nativeStep(long nativeContext, boolean directionDown, boolean skipSubChannel); nativeScan(long nativeContext, boolean directionDown, boolean skipSubChannel)82 private native void nativeScan(long nativeContext, boolean directionDown, boolean skipSubChannel); nativeTune(long nativeContext, @NonNull ProgramSelector selector)83 private native void nativeTune(long nativeContext, @NonNull ProgramSelector selector); nativeCancel(long nativeContext)84 private native void nativeCancel(long nativeContext); 85 nativeCancelAnnouncement(long nativeContext)86 private native void nativeCancelAnnouncement(long nativeContext); 87 nativeStartBackgroundScan(long nativeContext)88 private native boolean nativeStartBackgroundScan(long nativeContext); nativeGetProgramList(long nativeContext, Map<String, String> vendorFilter)89 private native List<RadioManager.ProgramInfo> nativeGetProgramList(long nativeContext, 90 Map<String, String> vendorFilter); 91 nativeGetImage(long nativeContext, int id)92 private native byte[] nativeGetImage(long nativeContext, int id); 93 nativeIsAnalogForced(long nativeContext)94 private native boolean nativeIsAnalogForced(long nativeContext); nativeSetAnalogForced(long nativeContext, boolean isForced)95 private native void nativeSetAnalogForced(long nativeContext, boolean isForced); 96 97 @Override close()98 public void close() { 99 synchronized (mLock) { 100 if (mIsClosed) return; 101 mIsClosed = true; 102 mTunerCallback.detach(); 103 mClientCallback.asBinder().unlinkToDeath(mDeathRecipient, 0); 104 nativeClose(mNativeContext); 105 } 106 } 107 108 @Override isClosed()109 public boolean isClosed() { 110 return mIsClosed; 111 } 112 checkNotClosedLocked()113 private void checkNotClosedLocked() { 114 if (mIsClosed) { 115 throw new IllegalStateException("Tuner is closed, no further operations are allowed"); 116 } 117 } 118 checkConfiguredLocked()119 private boolean checkConfiguredLocked() { 120 if (mTunerCallback.isInitialConfigurationDone()) return true; 121 Slog.w(TAG, "Initial configuration is still pending, skipping the operation"); 122 return false; 123 } 124 125 @Override setConfiguration(RadioManager.BandConfig config)126 public void setConfiguration(RadioManager.BandConfig config) { 127 if (config == null) { 128 throw new IllegalArgumentException("The argument must not be a null pointer"); 129 } 130 synchronized (mLock) { 131 checkNotClosedLocked(); 132 nativeSetConfiguration(mNativeContext, config); 133 mRegion = config.getRegion(); 134 } 135 } 136 137 @Override getConfiguration()138 public RadioManager.BandConfig getConfiguration() { 139 synchronized (mLock) { 140 checkNotClosedLocked(); 141 return nativeGetConfiguration(mNativeContext, mRegion); 142 } 143 } 144 145 @Override setMuted(boolean mute)146 public void setMuted(boolean mute) { 147 if (!mWithAudio) { 148 throw new IllegalStateException("Can't operate on mute - no audio requested"); 149 } 150 synchronized (mLock) { 151 checkNotClosedLocked(); 152 if (mIsMuted == mute) return; 153 mIsMuted = mute; 154 Slog.w(TAG, "Mute via RadioService is not implemented - please handle it via app"); 155 } 156 } 157 158 @Override isMuted()159 public boolean isMuted() { 160 if (!mWithAudio) { 161 Slog.w(TAG, "Tuner did not request audio, pretending it was muted"); 162 return true; 163 } 164 synchronized (mLock) { 165 checkNotClosedLocked(); 166 return mIsMuted; 167 } 168 } 169 170 @Override step(boolean directionDown, boolean skipSubChannel)171 public void step(boolean directionDown, boolean skipSubChannel) { 172 synchronized (mLock) { 173 checkNotClosedLocked(); 174 if (!checkConfiguredLocked()) return; 175 nativeStep(mNativeContext, directionDown, skipSubChannel); 176 } 177 } 178 179 @Override scan(boolean directionDown, boolean skipSubChannel)180 public void scan(boolean directionDown, boolean skipSubChannel) { 181 synchronized (mLock) { 182 checkNotClosedLocked(); 183 if (!checkConfiguredLocked()) return; 184 nativeScan(mNativeContext, directionDown, skipSubChannel); 185 } 186 } 187 188 @Override tune(ProgramSelector selector)189 public void tune(ProgramSelector selector) { 190 if (selector == null) { 191 throw new IllegalArgumentException("The argument must not be a null pointer"); 192 } 193 Slog.i(TAG, "Tuning to " + selector); 194 synchronized (mLock) { 195 checkNotClosedLocked(); 196 if (!checkConfiguredLocked()) return; 197 nativeTune(mNativeContext, selector); 198 } 199 } 200 201 @Override cancel()202 public void cancel() { 203 synchronized (mLock) { 204 checkNotClosedLocked(); 205 nativeCancel(mNativeContext); 206 } 207 } 208 209 @Override cancelAnnouncement()210 public void cancelAnnouncement() { 211 synchronized (mLock) { 212 checkNotClosedLocked(); 213 nativeCancelAnnouncement(mNativeContext); 214 } 215 } 216 217 @Override getImage(int id)218 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 startBackgroundScan()235 public boolean startBackgroundScan() { 236 synchronized (mLock) { 237 checkNotClosedLocked(); 238 return nativeStartBackgroundScan(mNativeContext); 239 } 240 } 241 getProgramList(Map vendorFilter)242 List<RadioManager.ProgramInfo> getProgramList(Map vendorFilter) { 243 Map<String, String> sFilter = vendorFilter; 244 synchronized (mLock) { 245 checkNotClosedLocked(); 246 List<RadioManager.ProgramInfo> list = nativeGetProgramList(mNativeContext, sFilter); 247 if (list == null) { 248 throw new IllegalStateException("Program list is not ready"); 249 } 250 return list; 251 } 252 } 253 254 @Override startProgramListUpdates(ProgramList.Filter filter)255 public void startProgramListUpdates(ProgramList.Filter filter) { 256 mTunerCallback.startProgramListUpdates(filter); 257 } 258 259 @Override stopProgramListUpdates()260 public void stopProgramListUpdates() { 261 mTunerCallback.stopProgramListUpdates(); 262 } 263 264 @Override isConfigFlagSupported(int flag)265 public boolean isConfigFlagSupported(int flag) { 266 return flag == RadioManager.CONFIG_FORCE_ANALOG; 267 } 268 269 @Override isConfigFlagSet(int flag)270 public boolean isConfigFlagSet(int flag) { 271 if (flag == RadioManager.CONFIG_FORCE_ANALOG) { 272 synchronized (mLock) { 273 checkNotClosedLocked(); 274 return nativeIsAnalogForced(mNativeContext); 275 } 276 } 277 throw new UnsupportedOperationException("Not supported by HAL 1.x"); 278 } 279 280 @Override setConfigFlag(int flag, boolean value)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"); 290 } 291 292 @Override setParameters(Map<String, String> parameters)293 public Map<String, String> setParameters(Map<String, String> parameters) { 294 throw new UnsupportedOperationException("Not supported by HAL 1.x"); 295 } 296 297 @Override getParameters(List<String> keys)298 public Map<String, String> getParameters(List<String> keys) { 299 throw new UnsupportedOperationException("Not supported by HAL 1.x"); 300 } 301 } 302