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