• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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