1 /* 2 * Copyright 2019 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 android.media.tv.tuner; 18 19 import android.annotation.IntDef; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.annotation.SystemApi; 23 import android.hardware.tv.tuner.LnbEventType; 24 import android.hardware.tv.tuner.LnbPosition; 25 import android.hardware.tv.tuner.LnbTone; 26 import android.hardware.tv.tuner.LnbVoltage; 27 import android.media.tv.tuner.Tuner.Result; 28 import android.media.tv.tunerresourcemanager.TunerResourceManager; 29 import android.util.Log; 30 31 import java.lang.annotation.Retention; 32 import java.lang.annotation.RetentionPolicy; 33 import java.util.HashMap; 34 import java.util.Map; 35 import java.util.Objects; 36 import java.util.concurrent.Executor; 37 38 /** 39 * LNB (low-noise block downconverter) for satellite tuner. 40 * 41 * A Tuner LNB (low-noise block downconverter) is used by satellite frontend to receive the 42 * microwave signal from the satellite, amplify it, and downconvert the frequency to a lower 43 * frequency. 44 * 45 * @hide 46 */ 47 @SystemApi 48 public class Lnb implements AutoCloseable { 49 /** @hide */ 50 @IntDef(prefix = "VOLTAGE_", 51 value = {VOLTAGE_NONE, VOLTAGE_5V, VOLTAGE_11V, VOLTAGE_12V, VOLTAGE_13V, VOLTAGE_14V, 52 VOLTAGE_15V, VOLTAGE_18V, VOLTAGE_19V}) 53 @Retention(RetentionPolicy.SOURCE) 54 public @interface Voltage {} 55 56 /** 57 * LNB power voltage not set. 58 */ 59 public static final int VOLTAGE_NONE = LnbVoltage.NONE; 60 /** 61 * LNB power voltage 5V. 62 */ 63 public static final int VOLTAGE_5V = LnbVoltage.VOLTAGE_5V; 64 /** 65 * LNB power voltage 11V. 66 */ 67 public static final int VOLTAGE_11V = LnbVoltage.VOLTAGE_11V; 68 /** 69 * LNB power voltage 12V. 70 */ 71 public static final int VOLTAGE_12V = LnbVoltage.VOLTAGE_12V; 72 /** 73 * LNB power voltage 13V. 74 */ 75 public static final int VOLTAGE_13V = LnbVoltage.VOLTAGE_13V; 76 /** 77 * LNB power voltage 14V. 78 */ 79 public static final int VOLTAGE_14V = LnbVoltage.VOLTAGE_14V; 80 /** 81 * LNB power voltage 15V. 82 */ 83 public static final int VOLTAGE_15V = LnbVoltage.VOLTAGE_15V; 84 /** 85 * LNB power voltage 18V. 86 */ 87 public static final int VOLTAGE_18V = LnbVoltage.VOLTAGE_18V; 88 /** 89 * LNB power voltage 19V. 90 */ 91 public static final int VOLTAGE_19V = LnbVoltage.VOLTAGE_19V; 92 93 /** @hide */ 94 @IntDef(prefix = "TONE_", 95 value = {TONE_NONE, TONE_CONTINUOUS}) 96 @Retention(RetentionPolicy.SOURCE) 97 public @interface Tone {} 98 99 /** 100 * LNB tone mode not set. 101 */ 102 public static final int TONE_NONE = LnbTone.NONE; 103 /** 104 * LNB continuous tone mode. 105 */ 106 public static final int TONE_CONTINUOUS = LnbTone.CONTINUOUS; 107 108 /** @hide */ 109 @IntDef(prefix = "POSITION_", 110 value = {POSITION_UNDEFINED, POSITION_A, POSITION_B}) 111 @Retention(RetentionPolicy.SOURCE) 112 public @interface Position {} 113 114 /** 115 * LNB position is not defined. 116 */ 117 public static final int POSITION_UNDEFINED = LnbPosition.UNDEFINED; 118 /** 119 * Position A of two-band LNBs 120 */ 121 public static final int POSITION_A = LnbPosition.POSITION_A; 122 /** 123 * Position B of two-band LNBs 124 */ 125 public static final int POSITION_B = LnbPosition.POSITION_B; 126 127 /** @hide */ 128 @Retention(RetentionPolicy.SOURCE) 129 @IntDef(prefix = "EVENT_TYPE_", 130 value = {EVENT_TYPE_DISEQC_RX_OVERFLOW, EVENT_TYPE_DISEQC_RX_TIMEOUT, 131 EVENT_TYPE_DISEQC_RX_PARITY_ERROR, EVENT_TYPE_LNB_OVERLOAD}) 132 public @interface EventType {} 133 134 /** 135 * Outgoing Diseqc message overflow. 136 */ 137 public static final int EVENT_TYPE_DISEQC_RX_OVERFLOW = LnbEventType.DISEQC_RX_OVERFLOW; 138 /** 139 * Outgoing Diseqc message isn't delivered on time. 140 */ 141 public static final int EVENT_TYPE_DISEQC_RX_TIMEOUT = LnbEventType.DISEQC_RX_TIMEOUT; 142 /** 143 * Incoming Diseqc message has parity error. 144 */ 145 public static final int EVENT_TYPE_DISEQC_RX_PARITY_ERROR = LnbEventType.DISEQC_RX_PARITY_ERROR; 146 /** 147 * LNB is overload. 148 */ 149 public static final int EVENT_TYPE_LNB_OVERLOAD = LnbEventType.LNB_OVERLOAD; 150 151 private static final String TAG = "Lnb"; 152 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 153 154 Map<LnbCallback, Executor> mCallbackMap = 155 new HashMap<LnbCallback, Executor>(); 156 Tuner mOwner; 157 TunerResourceManager mTunerResourceManager; 158 int mClientId; 159 private final Object mCallbackLock = new Object(); 160 161 nativeSetVoltage(int voltage)162 private native int nativeSetVoltage(int voltage); nativeSetTone(int tone)163 private native int nativeSetTone(int tone); nativeSetSatellitePosition(int position)164 private native int nativeSetSatellitePosition(int position); nativeSendDiseqcMessage(byte[] message)165 private native int nativeSendDiseqcMessage(byte[] message); nativeClose()166 private native int nativeClose(); 167 168 private long mNativeContext; 169 170 private Boolean mIsClosed = false; 171 private final Object mLock = new Object(); 172 Lnb()173 private Lnb() {} 174 setCallbackAndOwner(Tuner tuner, Executor executor, @Nullable LnbCallback callback)175 void setCallbackAndOwner(Tuner tuner, Executor executor, @Nullable LnbCallback callback) { 176 synchronized (mCallbackLock) { 177 if (callback != null && executor != null) { 178 addCallback(executor, callback); 179 } 180 } 181 setOwner(tuner); 182 if (mOwner != null) { 183 mTunerResourceManager = mOwner.getTunerResourceManager(); 184 mClientId = mOwner.getClientId(); 185 } 186 } 187 188 /** 189 * Adds LnbCallback 190 * 191 * @param executor the executor on which callback will be invoked. Cannot be null. 192 * @param callback the callback to receive notifications from LNB. 193 */ addCallback(@onNull Executor executor, @NonNull LnbCallback callback)194 public void addCallback(@NonNull Executor executor, @NonNull LnbCallback callback) { 195 Objects.requireNonNull(executor, "executor must not be null"); 196 Objects.requireNonNull(callback, "callback must not be null"); 197 synchronized (mCallbackLock) { 198 mCallbackMap.put(callback, executor); 199 } 200 } 201 202 /** 203 * Removes LnbCallback 204 * 205 * @param callback the callback be removed for callback 206 * 207 * @return {@code true} when successful. {@code false} otherwise. 208 */ removeCallback(@onNull LnbCallback callback)209 public boolean removeCallback(@NonNull LnbCallback callback) { 210 Objects.requireNonNull(callback, "callback must not be null"); 211 synchronized (mCallbackLock) { 212 boolean result = (mCallbackMap.remove(callback) != null); 213 return result; 214 } 215 } 216 217 // allow owner transfer independent of whether callback is registered or not setOwner(@onNull Tuner newOwner)218 /* package */ void setOwner(@NonNull Tuner newOwner) { 219 Objects.requireNonNull(newOwner, "newOwner must not be null"); 220 synchronized (mLock) { 221 mOwner = newOwner; 222 mTunerResourceManager = newOwner.getTunerResourceManager(); 223 mClientId = newOwner.getClientId(); 224 } 225 } 226 onEvent(int eventType)227 private void onEvent(int eventType) { 228 synchronized (mCallbackLock) { 229 for (LnbCallback callback : mCallbackMap.keySet()) { 230 Executor executor = mCallbackMap.get(callback); 231 if (callback != null && executor != null) { 232 executor.execute(() -> { 233 synchronized (mCallbackLock) { 234 if (callback != null) { 235 callback.onEvent(eventType); 236 } 237 } 238 }); 239 } 240 } 241 } 242 } 243 onDiseqcMessage(byte[] diseqcMessage)244 private void onDiseqcMessage(byte[] diseqcMessage) { 245 synchronized (mCallbackLock) { 246 for (LnbCallback callback : mCallbackMap.keySet()) { 247 Executor executor = mCallbackMap.get(callback); 248 if (callback != null && executor != null) { 249 executor.execute(() -> { 250 synchronized (mCallbackLock) { 251 if (callback != null) { 252 callback.onDiseqcMessage(diseqcMessage); 253 } 254 } 255 }); 256 } 257 } 258 } 259 } 260 isClosed()261 /* package */ boolean isClosed() { 262 synchronized (mLock) { 263 return mIsClosed; 264 } 265 } 266 closeInternal()267 /* package */ void closeInternal() { 268 synchronized (mLock) { 269 if (mIsClosed) { 270 return; 271 } 272 int res = nativeClose(); 273 if (res != Tuner.RESULT_SUCCESS) { 274 TunerUtils.throwExceptionForResult(res, "Failed to close LNB"); 275 } else { 276 mIsClosed = true; 277 if (mOwner != null) { 278 mOwner.releaseLnb(); 279 mOwner = null; 280 } 281 mCallbackMap.clear(); 282 } 283 } 284 } 285 286 /** 287 * Sets the LNB's power voltage. 288 * 289 * @param voltage the power voltage constant the Lnb to use. 290 * @return result status of the operation. 291 */ 292 @Result setVoltage(@oltage int voltage)293 public int setVoltage(@Voltage int voltage) { 294 synchronized (mLock) { 295 TunerUtils.checkResourceState(TAG, mIsClosed); 296 return nativeSetVoltage(voltage); 297 } 298 } 299 300 /** 301 * Sets the LNB's tone mode. 302 * 303 * @param tone the tone mode the Lnb to use. 304 * @return result status of the operation. 305 */ 306 @Result setTone(@one int tone)307 public int setTone(@Tone int tone) { 308 synchronized (mLock) { 309 TunerUtils.checkResourceState(TAG, mIsClosed); 310 return nativeSetTone(tone); 311 } 312 } 313 314 /** 315 * Selects the LNB's position. 316 * 317 * @param position the position the Lnb to use. 318 * @return result status of the operation. 319 */ 320 @Result setSatellitePosition(@osition int position)321 public int setSatellitePosition(@Position int position) { 322 synchronized (mLock) { 323 TunerUtils.checkResourceState(TAG, mIsClosed); 324 return nativeSetSatellitePosition(position); 325 } 326 } 327 328 /** 329 * Sends DiSEqC (Digital Satellite Equipment Control) message. 330 * 331 * The response message from the device comes back through callback onDiseqcMessage. 332 * 333 * @param message a byte array of data for DiSEqC message which is specified by EUTELSAT Bus 334 * Functional Specification Version 4.2. 335 * 336 * @return result status of the operation. 337 */ 338 @Result sendDiseqcMessage(@onNull byte[] message)339 public int sendDiseqcMessage(@NonNull byte[] message) { 340 synchronized (mLock) { 341 TunerUtils.checkResourceState(TAG, mIsClosed); 342 return nativeSendDiseqcMessage(message); 343 } 344 } 345 346 /** 347 * Releases the LNB instance. 348 */ close()349 public void close() { 350 acquireTRMSLock("close()"); 351 try { 352 closeInternal(); 353 } finally { 354 releaseTRMSLock(); 355 } 356 } 357 acquireTRMSLock(String functionNameForLog)358 private void acquireTRMSLock(String functionNameForLog) { 359 if (DEBUG) { 360 Log.d(TAG, "ATTEMPT:acquireLock() in " + functionNameForLog 361 + "for clientId:" + mClientId); 362 } 363 if (!mTunerResourceManager.acquireLock(mClientId)) { 364 Log.e(TAG, "FAILED:acquireLock() in " + functionNameForLog 365 + " for clientId:" + mClientId + " - this can cause deadlock between" 366 + " Tuner API calls and onReclaimResources()"); 367 } 368 } 369 releaseTRMSLock()370 private void releaseTRMSLock() { 371 mTunerResourceManager.releaseLock(mClientId); 372 } 373 } 374