1 /*
2  * Copyright 2020 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.tunerresourcemanager;
18 
19 import android.annotation.CallbackExecutor;
20 import android.annotation.IntDef;
21 import android.annotation.NonNull;
22 import android.annotation.Nullable;
23 import android.annotation.RequiresFeature;
24 import android.annotation.SystemService;
25 import android.content.Context;
26 import android.content.pm.PackageManager;
27 import android.os.Binder;
28 import android.os.RemoteException;
29 import android.util.Log;
30 
31 import java.lang.annotation.Retention;
32 import java.lang.annotation.RetentionPolicy;
33 import java.util.concurrent.Executor;
34 
35 /**
36  * Interface of the Tuner Resource Manager(TRM). It manages resources used by TV Tuners.
37  * <p>Resources include:
38  * <ul>
39  * <li>TunerFrontend {@link android.media.tv.tuner.frontend}.
40  * <li>TunerLnb {@link android.media.tv.tuner.Lnb}.
41  * <li>MediaCas {@link android.media.MediaCas}.
42  * <ul>
43  *
44  * <p>Expected workflow is:
45  * <ul>
46  * <li>Tuner Java/MediaCas/TIF update resources of the current device with TRM.
47  * <li>Client registers its profile through {@link #registerClientProfile(ResourceClientProfile,
48  * Executor, ResourcesReclaimListener, int[])}.
49  * <li>Client requests resources through request APIs.
50  * <li>If the resource needs to be handed to a higher priority client from a lower priority
51  * one, TRM calls IResourcesReclaimListener registered by the lower priority client to release
52  * the resource.
53  * <ul>
54  *
55  * <p>TRM also exposes its priority comparison algorithm as a helping method to other services.
56  * {@see #isHigherPriority(ResourceClientProfile, ResourceClientProfile)}.
57  *
58  * @hide
59  */
60 @RequiresFeature(PackageManager.FEATURE_LIVE_TV)
61 @SystemService(Context.TV_TUNER_RESOURCE_MGR_SERVICE)
62 public class TunerResourceManager {
63     private static final String TAG = "TunerResourceManager";
64     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
65 
66     public static final int INVALID_RESOURCE_HANDLE = -1;
67     public static final int INVALID_OWNER_ID = -1;
68     /**
69      * Tuner resource type to help generate resource handle
70      */
71     @IntDef({
72         TUNER_RESOURCE_TYPE_FRONTEND,
73         TUNER_RESOURCE_TYPE_DEMUX,
74         TUNER_RESOURCE_TYPE_DESCRAMBLER,
75         TUNER_RESOURCE_TYPE_LNB,
76         TUNER_RESOURCE_TYPE_CAS_SESSION,
77         TUNER_RESOURCE_TYPE_MAX,
78      })
79     @Retention(RetentionPolicy.SOURCE)
80     public @interface TunerResourceType {}
81 
82     public static final int TUNER_RESOURCE_TYPE_FRONTEND = 0;
83     public static final int TUNER_RESOURCE_TYPE_DEMUX = 1;
84     public static final int TUNER_RESOURCE_TYPE_DESCRAMBLER = 2;
85     public static final int TUNER_RESOURCE_TYPE_LNB = 3;
86     public static final int TUNER_RESOURCE_TYPE_CAS_SESSION = 4;
87     public static final int TUNER_RESOURCE_TYPE_MAX = 5;
88 
89     private final ITunerResourceManager mService;
90     private final int mUserId;
91 
92     /**
93      * @hide
94      */
TunerResourceManager(ITunerResourceManager service, int userId)95     public TunerResourceManager(ITunerResourceManager service, int userId) {
96         mService = service;
97         mUserId = userId;
98     }
99 
100     /**
101      * This API is used by the client to register their profile with the Tuner Resource manager.
102      *
103      * <p>The profile contains information that can show the base priority score of the client.
104      *
105      * @param profile {@link ResourceClientProfile} profile of the current client. Undefined use
106      *                case would cause IllegalArgumentException.
107      * @param executor the executor on which the listener would be invoked.
108      * @param listener {@link ResourcesReclaimListener} callback to reclaim clients' resources when
109      *                 needed.
110      * @param clientId returned a clientId from the resource manager when the
111      *                 the client registeres.
112      * @throws IllegalArgumentException when {@code profile} contains undefined use case.
113      */
registerClientProfile(@onNull ResourceClientProfile profile, @NonNull @CallbackExecutor Executor executor, @NonNull ResourcesReclaimListener listener, @NonNull int[] clientId)114     public void registerClientProfile(@NonNull ResourceClientProfile profile,
115                         @NonNull @CallbackExecutor Executor executor,
116                         @NonNull ResourcesReclaimListener listener,
117                         @NonNull int[] clientId) {
118         // TODO: throw new IllegalArgumentException("Unknown client use case")
119         // when the use case is not defined.
120         try {
121             mService.registerClientProfile(profile,
122                     new IResourcesReclaimListener.Stub() {
123                     @Override
124                 public void onReclaimResources() {
125                         final long identity = Binder.clearCallingIdentity();
126                         try {
127                             executor.execute(() -> listener.onReclaimResources());
128                         } finally {
129                             Binder.restoreCallingIdentity(identity);
130                         }
131                     }
132                 }, clientId);
133         } catch (RemoteException e) {
134             throw e.rethrowFromSystemServer();
135         }
136     }
137 
138     /**
139      * This API is used by the client to unregister their profile with the
140      * Tuner Resource manager.
141      *
142      * @param clientId the client id that needs to be unregistered.
143      */
unregisterClientProfile(int clientId)144     public void unregisterClientProfile(int clientId) {
145         try {
146             mService.unregisterClientProfile(clientId);
147         } catch (RemoteException e) {
148             throw e.rethrowFromSystemServer();
149         }
150     }
151 
152     /**
153      * This API is used by client to update its registered {@link ResourceClientProfile}.
154      *
155      * <p>We recommend creating a new tuner instance for different use cases instead of using this
156      * API since different use cases may need different resources.
157      *
158      * <p>If TIS updates use case, it needs to ensure underneath resources are exchangeable between
159      * two different use cases.
160      *
161      * <p>Only the arbitrary priority and niceValue are allowed to be updated.
162      *
163      * @param clientId the id of the client that is updating its profile.
164      * @param priority the priority that the client would like to update to.
165      * @param niceValue the nice value that the client would like to update to.
166      *
167      * @return true if the update is successful.
168      */
updateClientPriority(int clientId, int priority, int niceValue)169     public boolean updateClientPriority(int clientId, int priority, int niceValue) {
170         boolean result = false;
171         try {
172             result = mService.updateClientPriority(clientId, priority, niceValue);
173         } catch (RemoteException e) {
174             throw e.rethrowFromSystemServer();
175         }
176         return result;
177     }
178 
179     /**
180      * Updates the current TRM of the TunerHAL Frontend information.
181      *
182      * <p><strong>Note:</strong> This update must happen before the first
183      * {@link #requestFrontend(TunerFrontendRequest, int[])} and
184      * {@link #releaseFrontend(int, int)} call.
185      *
186      * @param infos an array of the available {@link TunerFrontendInfo} information.
187      */
setFrontendInfoList(@onNull TunerFrontendInfo[] infos)188     public void setFrontendInfoList(@NonNull TunerFrontendInfo[] infos) {
189         try {
190             mService.setFrontendInfoList(infos);
191         } catch (RemoteException e) {
192             throw e.rethrowFromSystemServer();
193         }
194     }
195 
196     /**
197      * Updates the TRM of the current CAS information.
198      *
199      * <p><strong>Note:</strong> This update must happen before the first
200      * {@link #requestCasSession(CasSessionRequest, int[])} and {@link #releaseCasSession(int, int)}
201      * call.
202      *
203      * @param casSystemId id of the updating CAS system.
204      * @param maxSessionNum the max session number of the CAS system that is updated.
205      */
updateCasInfo(int casSystemId, int maxSessionNum)206     public void updateCasInfo(int casSystemId, int maxSessionNum) {
207         try {
208             mService.updateCasInfo(casSystemId, maxSessionNum);
209         } catch (RemoteException e) {
210             throw e.rethrowFromSystemServer();
211         }
212     }
213 
214     /**
215      * Updates the TRM of the current Lnb information.
216      *
217      * <p><strong>Note:</strong> This update must happen before the first
218      * {@link #requestLnb(TunerLnbRequest, int[])} and {@link #releaseLnb(int, int)} call.
219      *
220      * @param lnbIds ids of the updating lnbs.
221      */
setLnbInfoList(int[] lnbIds)222     public void setLnbInfoList(int[] lnbIds) {
223         try {
224             mService.setLnbInfoList(lnbIds);
225         } catch (RemoteException e) {
226             throw e.rethrowFromSystemServer();
227         }
228     }
229 
230     /**
231      * Requests a frontend resource.
232      *
233      * <p>There are three possible scenarios:
234      * <ul>
235      * <li>If there is frontend available, the API would send the id back.
236      *
237      * <li>If no Frontend is available but the current request info can show higher priority than
238      * other uses of Frontend, the API will send
239      * {@link IResourcesReclaimListener#onReclaimResources()} to the {@link Tuner}. Tuner would
240      * handle the resource reclaim on the holder of lower priority and notify the holder of its
241      * resource loss.
242      *
243      * <li>If no frontend can be granted, the API would return false.
244      * <ul>
245      *
246      * <p><strong>Note:</strong> {@link #setFrontendInfoList(TunerFrontendInfo[])} must be called
247      * before this request.
248      *
249      * @param request {@link TunerFrontendRequest} information of the current request.
250      * @param frontendHandle a one-element array to return the granted frontendHandle. If
251      *                       no frontend granted, this will return {@link #INVALID_RESOURCE_HANDLE}.
252      *
253      * @return true if there is frontend granted.
254      */
requestFrontend(@onNull TunerFrontendRequest request, @Nullable int[] frontendHandle)255     public boolean requestFrontend(@NonNull TunerFrontendRequest request,
256                 @Nullable int[] frontendHandle) {
257         boolean result = false;
258         try {
259             result = mService.requestFrontend(request, frontendHandle);
260         } catch (RemoteException e) {
261             throw e.rethrowFromSystemServer();
262         }
263         return result;
264     }
265 
266     /**
267      * Requests from the client to share frontend with an existing client.
268      *
269      * <p><strong>Note:</strong> {@link #setFrontendInfoList(TunerFrontendInfo[])} must be called
270      * before this request.
271      *
272      * @param selfClientId the id of the client that sends the request.
273      * @param targetClientId the id of the client to share the frontend with.
274      */
shareFrontend(int selfClientId, int targetClientId)275     public void shareFrontend(int selfClientId, int targetClientId) {
276         try {
277             mService.shareFrontend(selfClientId, targetClientId);
278         } catch (RemoteException e) {
279             throw e.rethrowFromSystemServer();
280         }
281     }
282 
283     /**
284      * Requests a Tuner Demux resource.
285      *
286      * <p>There are three possible scenarios:
287      * <ul>
288      * <li>If there is Demux available, the API would send the handle back.
289      *
290      * <li>If no Demux is available but the current request has a higher priority than other uses of
291      * demuxes, the API will send {@link IResourcesReclaimListener#onReclaimResources()} to the
292      * {@link Tuner}. Tuner would handle the resource reclaim on the holder of lower priority and
293      * notify the holder of its resource loss.
294      *
295      * <li>If no Demux system can be granted, the API would return false.
296      * <ul>
297      *
298      * @param request {@link TunerDemuxRequest} information of the current request.
299      * @param demuxHandle a one-element array to return the granted Demux handle.
300      *                    If no Demux granted, this will return {@link #INVALID_RESOURCE_HANDLE}.
301      *
302      * @return true if there is Demux granted.
303      */
requestDemux(@onNull TunerDemuxRequest request, @NonNull int[] demuxHandle)304     public boolean requestDemux(@NonNull TunerDemuxRequest request, @NonNull int[] demuxHandle) {
305         boolean result = false;
306         try {
307             result = mService.requestDemux(request, demuxHandle);
308         } catch (RemoteException e) {
309             throw e.rethrowFromSystemServer();
310         }
311         return result;
312     }
313 
314     /**
315      * Requests a Tuner Descrambler resource.
316      *
317      * <p>There are three possible scenarios:
318      * <ul>
319      * <li>If there is Descrambler available, the API would send the handle back.
320      *
321      * <li>If no Descrambler is available but the current request has a higher priority than other
322      * uses of descramblers, the API will send
323      * {@link IResourcesReclaimListener#onReclaimResources()} to the {@link Tuner}. Tuner would
324      * handle the resource reclaim on the holder of lower priority and notify the holder of its
325      * resource loss.
326      *
327      * <li>If no Descrambler system can be granted, the API would return false.
328      * <ul>
329      *
330      * @param request {@link TunerDescramblerRequest} information of the current request.
331      * @param descramblerHandle a one-element array to return the granted Descrambler handle.
332      *                          If no Descrambler granted, this will return
333      *                          {@link #INVALID_RESOURCE_HANDLE}.
334      *
335      * @return true if there is Descrambler granted.
336      */
requestDescrambler(@onNull TunerDescramblerRequest request, @NonNull int[] descramblerHandle)337     public boolean requestDescrambler(@NonNull TunerDescramblerRequest request,
338                 @NonNull int[] descramblerHandle) {
339         boolean result = false;
340         try {
341             result = mService.requestDescrambler(request, descramblerHandle);
342         } catch (RemoteException e) {
343             throw e.rethrowFromSystemServer();
344         }
345         return result;
346     }
347 
348     /**
349      * Requests a CAS session resource.
350      *
351      * <p>There are three possible scenarios:
352      * <ul>
353      * <li>If there is Cas session available, the API would send the id back.
354      *
355      * <li>If no Cas system is available but the current request info can show higher priority than
356      * other uses of the cas sessions under the requested cas system, the API will send
357      * {@link IResourcesReclaimListener#onReclaimResources()} to the {@link Tuner}. Tuner would
358      * handle the resource reclaim on the holder of lower priority and notify the holder of its
359      * resource loss.
360      *
361      * <p><strong>Note:</strong> {@link #updateCasInfo(int, int)} must be called before this
362      * request.
363      *
364      * @param request {@link CasSessionRequest} information of the current request.
365      * @param casSessionHandle a one-element array to return the granted cas session handel.
366      *                         If no CAS granted, this will return {@link #INVALID_RESOURCE_HANDLE}.
367      *
368      * @return true if there is CAS session granted.
369      */
requestCasSession(@onNull CasSessionRequest request, @NonNull int[] casSessionHandle)370     public boolean requestCasSession(@NonNull CasSessionRequest request,
371                 @NonNull int[] casSessionHandle) {
372         boolean result = false;
373         try {
374             result = mService.requestCasSession(request, casSessionHandle);
375         } catch (RemoteException e) {
376             throw e.rethrowFromSystemServer();
377         }
378         return result;
379     }
380 
381     /**
382      * Requests a Tuner Lnb resource.
383      *
384      * <p>There are three possible scenarios:
385      * <ul>
386      * <li>If there is Lnb available, the API would send the id back.
387      *
388      * <li>If no Lnb is available but the current request has a higher priority than other uses of
389      * lnbs, the API will send {@link IResourcesReclaimListener#onReclaimResources()} to the
390      * {@link Tuner}. Tuner would handle the resource reclaim on the holder of lower priority and
391      * notify the holder of its resource loss.
392      *
393      * <li>If no Lnb system can be granted, the API would return false.
394      * <ul>
395      *
396      * <p><strong>Note:</strong> {@link #setLnbInfoList(int[])} must be called before this request.
397      *
398      * @param request {@link TunerLnbRequest} information of the current request.
399      * @param lnbHandle a one-element array to return the granted Lnb handle.
400      *                  If no Lnb granted, this will return {@link #INVALID_RESOURCE_HANDLE}.
401      *
402      * @return true if there is Lnb granted.
403      */
requestLnb(@onNull TunerLnbRequest request, @NonNull int[] lnbHandle)404     public boolean requestLnb(@NonNull TunerLnbRequest request, @NonNull int[] lnbHandle) {
405         boolean result = false;
406         try {
407             result = mService.requestLnb(request, lnbHandle);
408         } catch (RemoteException e) {
409             throw e.rethrowFromSystemServer();
410         }
411         return result;
412     }
413 
414     /**
415      * Notifies the TRM that the given frontend has been released.
416      *
417      * <p>Client must call this whenever it releases a Tuner frontend.
418      *
419      * <p><strong>Note:</strong> {@link #setFrontendInfoList(TunerFrontendInfo[])} must be called
420      * before this release.
421      *
422      * @param frontendHandle the handle of the released frontend.
423      * @param clientId the id of the client that is releasing the frontend.
424      */
releaseFrontend(int frontendHandle, int clientId)425     public void releaseFrontend(int frontendHandle, int clientId) {
426         try {
427             mService.releaseFrontend(frontendHandle, clientId);
428         } catch (RemoteException e) {
429             throw e.rethrowFromSystemServer();
430         }
431     }
432 
433     /**
434      * Notifies the TRM that the Demux with the given handle has been released.
435      *
436      * <p>Client must call this whenever it releases an Demux.
437      *
438      * @param demuxHandle the handle of the released Tuner Demux.
439      * @param clientId the id of the client that is releasing the demux.
440      */
releaseDemux(int demuxHandle, int clientId)441     public void releaseDemux(int demuxHandle, int clientId) {
442         try {
443             mService.releaseDemux(demuxHandle, clientId);
444         } catch (RemoteException e) {
445             throw e.rethrowFromSystemServer();
446         }
447     }
448 
449     /**
450      * Notifies the TRM that the Descrambler with the given handle has been released.
451      *
452      * <p>Client must call this whenever it releases an Descrambler.
453      *
454      * @param descramblerHandle the handle of the released Tuner Descrambler.
455      * @param clientId the id of the client that is releasing the descrambler.
456      */
releaseDescrambler(int descramblerHandle, int clientId)457     public void releaseDescrambler(int descramblerHandle, int clientId) {
458         try {
459             mService.releaseDescrambler(descramblerHandle, clientId);
460         } catch (RemoteException e) {
461             throw e.rethrowFromSystemServer();
462         }
463     }
464 
465     /**
466      * Notifies the TRM that the given Cas session has been released.
467      *
468      * <p>Client must call this whenever it releases a Cas session.
469      *
470      * <p><strong>Note:</strong> {@link #updateCasInfo(int, int)} must be called before this
471      * release.
472      *
473      * @param casSessionHandle the handle of the released CAS session.
474      * @param clientId the id of the client that is releasing the cas session.
475      */
releaseCasSession(int casSessionHandle, int clientId)476     public void releaseCasSession(int casSessionHandle, int clientId) {
477         try {
478             mService.releaseCasSession(casSessionHandle, clientId);
479         } catch (RemoteException e) {
480             throw e.rethrowFromSystemServer();
481         }
482     }
483 
484     /**
485      * Notifies the TRM that the Lnb with the given id has been released.
486      *
487      * <p>Client must call this whenever it releases an Lnb.
488      *
489      * <p><strong>Note:</strong> {@link #setLnbInfoList(int[])} must be called before this release.
490      *
491      * @param lnbHandle the handle of the released Tuner Lnb.
492      * @param clientId the id of the client that is releasing the lnb.
493      */
releaseLnb(int lnbHandle, int clientId)494     public void releaseLnb(int lnbHandle, int clientId) {
495         try {
496             mService.releaseLnb(lnbHandle, clientId);
497         } catch (RemoteException e) {
498             throw e.rethrowFromSystemServer();
499         }
500     }
501 
502     /**
503      * Compare two clients' priority.
504      *
505      * @param challengerProfile the {@link ResourceClientProfile} of the challenger.
506      * @param holderProfile the {@link ResourceClientProfile} of the holder of the resource.
507      *
508      * @return true if the challenger has higher priority than the holder.
509      */
isHigherPriority(ResourceClientProfile challengerProfile, ResourceClientProfile holderProfile)510     public boolean isHigherPriority(ResourceClientProfile challengerProfile,
511             ResourceClientProfile holderProfile) {
512         try {
513             return mService.isHigherPriority(challengerProfile, holderProfile);
514         } catch (RemoteException e) {
515             throw e.rethrowFromSystemServer();
516         }
517     }
518 
519     /**
520      * Interface used to receive events from TunerResourceManager.
521      */
522     public abstract static class ResourcesReclaimListener {
523         /*
524          * To reclaim all the resources of the callack owner.
525          */
onReclaimResources()526         public abstract void onReclaimResources();
527     }
528 }
529