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