From 5cba7b1136a38b721db6d3bc2d49b0b5ef983192 Mon Sep 17 00:00:00 2001 From: Henry Fang Date: Thu, 13 Feb 2020 10:36:17 -0800 Subject: [PATCH] Use Tuner Resource Manager to get resource permission Cooperate with Tuner Resource Manager to allocate and free CAS session resource bug: 141783130 Test: Manual Change-Id: I2fb09d8def0fcfa19d568e8c618b1824158ceda9 --- api/current.txt | 2 +- media/java/android/media/MediaCas.java | 124 ++++++++++++++++-- .../java/android/media/tv/TvInputService.java | 25 ++-- 3 files changed, 123 insertions(+), 28 deletions(-) diff --git a/api/current.txt b/api/current.txt index 140b6ae51a80a..4bbb6bfc3298a 100644 --- a/api/current.txt +++ b/api/current.txt @@ -25092,7 +25092,7 @@ package android.media { public final class MediaCas implements java.lang.AutoCloseable { ctor public MediaCas(int) throws android.media.MediaCasException.UnsupportedCasException; - ctor public MediaCas(int, @Nullable String, int) throws android.media.MediaCasException.UnsupportedCasException; + ctor public MediaCas(@NonNull android.content.Context, int, @Nullable String, int) throws android.media.MediaCasException.UnsupportedCasException; method public void close(); method public static android.media.MediaCas.PluginDescriptor[] enumeratePlugins(); method protected void finalize(); diff --git a/media/java/android/media/MediaCas.java b/media/java/android/media/MediaCas.java index cefc9db779243..3a771bba03b9d 100644 --- a/media/java/android/media/MediaCas.java +++ b/media/java/android/media/MediaCas.java @@ -19,14 +19,20 @@ package android.media; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.content.Context; import android.hardware.cas.V1_0.HidlCasPluginDescriptor; import android.hardware.cas.V1_0.ICas; import android.hardware.cas.V1_0.IMediaCasService; import android.hardware.cas.V1_2.ICasListener; +import android.hardware.cas.V1_2.Status; import android.media.MediaCasException.*; import android.media.tv.TvInputService.PriorityHintUseCaseType; +import android.media.tv.tunerresourcemanager.CasSessionRequest; +import android.media.tv.tunerresourcemanager.ResourceClientProfile; +import android.media.tv.tunerresourcemanager.TunerResourceManager; import android.os.Bundle; import android.os.Handler; +import android.os.HandlerExecutor; import android.os.HandlerThread; import android.os.IHwBinder; import android.os.Looper; @@ -39,6 +45,9 @@ import android.util.Singleton; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; /** * MediaCas can be used to obtain keys for descrambling protected media streams, in @@ -110,6 +119,10 @@ public final class MediaCas implements AutoCloseable { private EventHandler mEventHandler; private @PriorityHintUseCaseType int mPriorityHint; private String mTvInputServiceSessionId; + private int mClientId; + private int mCasSystemId; + private TunerResourceManager mTunerResourceManager = null; + private final Map mSessionMap = new HashMap<>(); /** * Scrambling modes used to open cas sessions. @@ -329,6 +342,10 @@ public final class MediaCas implements AutoCloseable { createFromSessionId(sessionId), msg.arg1, msg.arg2, bundle.getByteArray(DATA_KEY)); } else if (msg.what == MSG_CAS_STATUS_EVENT) { + if ((msg.arg1 == PLUGIN_STATUS_SESSION_NUMBER_CHANGED) + && (mTunerResourceManager != null)) { + mTunerResourceManager.updateCasInfo(mCasSystemId, msg.arg2); + } mListener.onPluginStatusUpdate(MediaCas.this, msg.arg1, msg.arg2); } else if (msg.what == MSG_CAS_RESOURCE_LOST) { mListener.onResourceLost(MediaCas.this); @@ -364,6 +381,19 @@ public final class MediaCas implements AutoCloseable { EventHandler.MSG_CAS_STATUS_EVENT, status, arg)); } }; + + private final TunerResourceManager.ResourcesReclaimListener mResourceListener = + new TunerResourceManager.ResourcesReclaimListener() { + @Override + public void onReclaimResources() { + synchronized (mSessionMap) { + mSessionMap.forEach((casSession, sessionResourceId) -> casSession.close()); + } + mEventHandler.sendMessage(mEventHandler.obtainMessage( + EventHandler.MSG_CAS_RESOURCE_LOST)); + } + }; + /** * Describe a CAS plugin with its CA_system_ID and string name. * @@ -429,11 +459,21 @@ public final class MediaCas implements AutoCloseable { */ public final class Session implements AutoCloseable { final ArrayList mSessionId; + boolean mIsClosed = false; Session(@NonNull ArrayList sessionId) { mSessionId = new ArrayList(sessionId); } + private void validateSessionInternalStates() { + if (mICas == null) { + throw new IllegalStateException(); + } + if (mIsClosed) { + MediaCasStateException.throwExceptionIfNeeded(Status.ERROR_CAS_SESSION_NOT_OPENED); + } + } + /** * Query if an object equal current Session object. * @@ -459,7 +499,7 @@ public final class MediaCas implements AutoCloseable { */ public void setPrivateData(@NonNull byte[] data) throws MediaCasException { - validateInternalStates(); + validateSessionInternalStates(); try { MediaCasException.throwExceptionIfNeeded( @@ -483,7 +523,7 @@ public final class MediaCas implements AutoCloseable { */ public void processEcm(@NonNull byte[] data, int offset, int length) throws MediaCasException { - validateInternalStates(); + validateSessionInternalStates(); try { MediaCasException.throwExceptionIfNeeded( @@ -522,7 +562,7 @@ public final class MediaCas implements AutoCloseable { */ public void sendSessionEvent(int event, int arg, @Nullable byte[] data) throws MediaCasException { - validateInternalStates(); + validateSessionInternalStates(); if (mICasV11 == null) { Log.d(TAG, "Send Session Event isn't supported by cas@1.0 interface"); @@ -546,7 +586,7 @@ public final class MediaCas implements AutoCloseable { */ @NonNull public byte[] getSessionId() { - validateInternalStates(); + validateSessionInternalStates(); return toBytes(mSessionId); } @@ -558,11 +598,12 @@ public final class MediaCas implements AutoCloseable { */ @Override public void close() { - validateInternalStates(); - + validateSessionInternalStates(); try { MediaCasStateException.throwExceptionIfNeeded( mICas.closeSession(mSessionId)); + mIsClosed = true; + removeSessionFromResourceMap(this); } catch (RemoteException e) { cleanupAndRethrowIllegalState(); } @@ -664,6 +705,7 @@ public final class MediaCas implements AutoCloseable { /** * Instantiate a CA system of the specified system id. * + * @param context the context of the caller. * @param casSystemId The system id of the CA system. * @param tvInputServiceSessionId The Id of the session opened in TV Input Service (TIS) * {@link android.media.tv.TvInputService#onCreateSession(String, String)} @@ -672,11 +714,23 @@ public final class MediaCas implements AutoCloseable { * @throws UnsupportedCasException if the device does not support the * specified CA system. */ - public MediaCas(int casSystemId, @Nullable String tvInputServiceSessionId, - @PriorityHintUseCaseType int priorityHint) throws UnsupportedCasException { + public MediaCas(@NonNull Context context, int casSystemId, + @Nullable String tvInputServiceSessionId, + @PriorityHintUseCaseType int priorityHint) throws UnsupportedCasException { this(casSystemId); - mPriorityHint = priorityHint; - mTvInputServiceSessionId = tvInputServiceSessionId; + + Objects.requireNonNull(context, "context must not be null"); + mCasSystemId = casSystemId; + mTunerResourceManager = (TunerResourceManager) + context.getSystemService(Context.TV_TUNER_RESOURCE_MGR_SERVICE); + if (mTunerResourceManager != null) { + int[] clientId = new int[1]; + ResourceClientProfile profile = + new ResourceClientProfile(tvInputServiceSessionId, priorityHint); + mTunerResourceManager.registerClientProfile( + profile, new HandlerExecutor(mEventHandler), mResourceListener, clientId); + mClientId = clientId[0]; + } } IHwBinder getBinder() { @@ -813,6 +867,40 @@ public final class MediaCas implements AutoCloseable { } } + private int getSessionResourceId() throws MediaCasException { + validateInternalStates(); + + int[] sessionResourceId = new int[1]; + sessionResourceId[0] = -1; + if (mTunerResourceManager != null) { + CasSessionRequest casSessionRequest = new CasSessionRequest(mClientId, mCasSystemId); + if (!mTunerResourceManager.requestCasSession(casSessionRequest, sessionResourceId)) { + throw new MediaCasException.ResourceBusyException( + "insufficient resource to Open Session"); + } + } + return sessionResourceId[0]; + } + + private void addSessionToResourceMap(Session session, int sessionResourceId) { + + if (sessionResourceId != -1) { + synchronized (mSessionMap) { + mSessionMap.put(session, sessionResourceId); + } + } + } + + private void removeSessionFromResourceMap(Session session) { + + synchronized (mSessionMap) { + if (mSessionMap.get(session) != null) { + mTunerResourceManager.releaseCasSession(mSessionMap.get(session)); + mSessionMap.remove(session); + } + } + } + /** * Open a session to descramble one or more streams scrambled by the * conditional access system. @@ -824,12 +912,13 @@ public final class MediaCas implements AutoCloseable { * @throws MediaCasStateException for CAS-specific state exceptions. */ public Session openSession() throws MediaCasException { - validateInternalStates(); + int sessionResourceId = getSessionResourceId(); try { OpenSessionCallback cb = new OpenSessionCallback(); mICas.openSession(cb); MediaCasException.throwExceptionIfNeeded(cb.mStatus); + addSessionToResourceMap(cb.mSession, sessionResourceId); return cb.mSession; } catch (RemoteException e) { cleanupAndRethrowIllegalState(); @@ -853,7 +942,7 @@ public final class MediaCas implements AutoCloseable { @Nullable public Session openSession(@SessionUsage int sessionUsage, @ScramblingMode int scramblingMode) throws MediaCasException { - validateInternalStates(); + int sessionResourceId = getSessionResourceId(); if (mICasV12 == null) { Log.d(TAG, "Open Session with scrambling mode is only supported by cas@1.2+ interface"); @@ -864,6 +953,7 @@ public final class MediaCas implements AutoCloseable { OpenSession_1_2_Callback cb = new OpenSession_1_2_Callback(); mICasV12.openSession_1_2(sessionUsage, scramblingMode, cb); MediaCasException.throwExceptionIfNeeded(cb.mStatus); + addSessionToResourceMap(cb.mSession, sessionResourceId); return cb.mSession; } catch (RemoteException e) { cleanupAndRethrowIllegalState(); @@ -987,6 +1077,16 @@ public final class MediaCas implements AutoCloseable { mICas = null; } } + + if (mTunerResourceManager != null) { + mTunerResourceManager.unregisterClientProfile(mClientId); + mTunerResourceManager = null; + } + + if (mHandlerThread != null) { + mHandlerThread.quit(); + mHandlerThread = null; + } } @Override diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java index 62c7e51f3edf0..b579144b45e82 100755 --- a/media/java/android/media/tv/TvInputService.java +++ b/media/java/android/media/tv/TvInputService.java @@ -111,37 +111,32 @@ public abstract class TvInputService extends Service { public @interface PriorityHintUseCaseType {} /** - * Use case of priority hint for {@link android.media.MediaCas#MediaCas(int, String , int)}: - * Background. - * TODO Link: Tuner#Tuner(Context, string, int). + * Use case of priority hint for {@link android.media.MediaCas#MediaCas(Context, int, String, + * int)}: Background. TODO Link: Tuner#Tuner(Context, string, int). */ public static final int PRIORITY_HINT_USE_CASE_TYPE_BACKGROUND = 100; /** - * Use case of priority hint for {@link android.media.MediaCas#MediaCas(int, String , int)}: - * Scan. - * TODO Link: Tuner#Tuner(Context, string, int). + * Use case of priority hint for {@link android.media.MediaCas#MediaCas(Context, int, String, + * int)}: Scan. TODO Link: Tuner#Tuner(Context, string, int). */ public static final int PRIORITY_HINT_USE_CASE_TYPE_SCAN = 200; /** - * Use case of priority hint for {@link android.media.MediaCas#MediaCas(int, String , int)}: - * Playback. - * TODO Link: Tuner#Tuner(Context, string, int). + * Use case of priority hint for {@link android.media.MediaCas#MediaCas(Context, int, String, + * int)}: Playback. TODO Link: Tuner#Tuner(Context, string, int). */ public static final int PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK = 300; /** - * Use case of priority hint for {@link android.media.MediaCas#MediaCas(int, String , int)}: - * Live. - * TODO Link: Tuner#Tuner(Context, string, int). + * Use case of priority hint for {@link android.media.MediaCas#MediaCas(Context, int, String, + * int)}: Live. TODO Link: Tuner#Tuner(Context, string, int). */ public static final int PRIORITY_HINT_USE_CASE_TYPE_LIVE = 400; /** - * Use case of priority hint for {@link android.media.MediaCas#MediaCas(int, String , int)}: - * Record. - * TODO Link: Tuner#Tuner(Context, string, int). + * Use case of priority hint for {@link android.media.MediaCas#MediaCas(Context, int, String, + * int)}: Record. TODO Link: Tuner#Tuner(Context, string, int). */ public static final int PRIORITY_HINT_USE_CASE_TYPE_RECORD = 500;