1d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang/*
2d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang * Copyright (C) 2017 The Android Open Source Project
3d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang *
4d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang * Licensed under the Apache License, Version 2.0 (the "License");
5d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang * you may not use this file except in compliance with the License.
6d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang * You may obtain a copy of the License at
7d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang *
8d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang *      http://www.apache.org/licenses/LICENSE-2.0
9d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang *
10d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang * Unless required by applicable law or agreed to in writing, software
11d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang * distributed under the License is distributed on an "AS IS" BASIS,
12d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang * See the License for the specific language governing permissions and
14d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang * limitations under the License.
15d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang */
16d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang
17d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhangpackage android.media;
18d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang
19d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhangimport android.annotation.NonNull;
20d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhangimport android.annotation.Nullable;
212659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhangimport android.hardware.cas.V1_0.*;
22dadee0c33e6cb659978ce4fa6aa21f7c384fc43cChong Zhangimport android.media.MediaCasException.*;
23d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhangimport android.os.Handler;
24d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhangimport android.os.HandlerThread;
252659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhangimport android.os.IHwBinder;
26d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhangimport android.os.Looper;
27d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhangimport android.os.Message;
28d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhangimport android.os.Process;
29d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhangimport android.os.RemoteException;
30d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhangimport android.util.Log;
31d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhangimport android.util.Singleton;
32d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang
332659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhangimport java.util.ArrayList;
342659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhang
35d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang/**
36d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang * MediaCas can be used to obtain keys for descrambling protected media streams, in
37d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang * conjunction with {@link android.media.MediaDescrambler}. The MediaCas APIs are
38d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang * designed to support conditional access such as those in the ISO/IEC13818-1.
39d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang * The CA system is identified by a 16-bit integer CA_system_id. The scrambling
40d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang * algorithms are usually proprietary and implemented by vendor-specific CA plugins
41d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang * installed on the device.
42d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang * <p>
43d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang * The app is responsible for constructing a MediaCas object for the CA system it
44d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang * intends to use. The app can query if a certain CA system is supported using static
45d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang * method {@link #isSystemIdSupported}. It can also obtain the entire list of supported
46d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang * CA systems using static method {@link #enumeratePlugins}.
47d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang * <p>
48d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang * Once the MediaCas object is constructed, the app should properly provision it by
49d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang * using method {@link #provision} and/or {@link #processEmm}. The EMMs (Entitlement
50d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang * management messages) can be distributed out-of-band, or in-band with the stream.
51d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang * <p>
52d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang * To descramble elementary streams, the app first calls {@link #openSession} to
53addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang * generate a {@link Session} object that will uniquely identify a session. A session
54addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang * provides a context for subsequent key updates and descrambling activities. The ECMs
55addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang * (Entitlement control messages) are sent to the session via method
56addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang * {@link Session#processEcm}.
57d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang * <p>
58d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang * The app next constructs a MediaDescrambler object, and initializes it with the
59addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang * session using {@link MediaDescrambler#setMediaCasSession}. This ties the
60d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang * descrambler to the session, and the descrambler can then be used to descramble
61d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang * content secured with the session's key, either during extraction, or during decoding
62d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang * with {@link android.media.MediaCodec}.
63d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang * <p>
64d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang * If the app handles sample extraction using its own extractor, it can use
65d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang * MediaDescrambler to descramble samples into clear buffers (if the session's license
66d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang * doesn't require secure decoders), or descramble a small amount of data to retrieve
67d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang * information necessary for the downstream pipeline to process the sample (if the
68d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang * session's license requires secure decoders).
69d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang * <p>
70d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang * If the session requires a secure decoder, a MediaDescrambler needs to be provided to
71d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang * MediaCodec to descramble samples queued by {@link MediaCodec#queueSecureInputBuffer}
72d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang * into protected buffers. The app should use {@link MediaCodec#configure(MediaFormat,
73d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang * android.view.Surface, int, MediaDescrambler)} instead of the normal {@link
74d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang * MediaCodec#configure(MediaFormat, android.view.Surface, MediaCrypto, int)} method
75d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang * to configure MediaCodec.
76d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang * <p>
77d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang * <h3>Using Android's MediaExtractor</h3>
78d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang * <p>
79d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang * If the app uses {@link MediaExtractor}, it can delegate the CAS session
80d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang * management to MediaExtractor by calling {@link MediaExtractor#setMediaCas}.
81d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang * MediaExtractor will take over and call {@link #openSession}, {@link #processEmm}
82addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang * and/or {@link Session#processEcm}, etc.. if necessary.
83d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang * <p>
84d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang * When using {@link MediaExtractor}, the app would still need a MediaDescrambler
85d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang * to use with {@link MediaCodec} if the licensing requires a secure decoder. The
86addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang * session associated with the descrambler of a track can be retrieved by calling
87addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang * {@link MediaExtractor#getCasInfo}, and used to initialize a MediaDescrambler
88addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang * object for MediaCodec.
89d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang * <p>
90d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang * <h3>Listeners</h3>
91d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang * <p>The app may register a listener to receive events from the CA system using
92d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang * method {@link #setEventListener}. The exact format of the event is scheme-specific
93d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang * and is not specified by this API.
94d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang */
95addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhangpublic final class MediaCas implements AutoCloseable {
96d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang    private static final String TAG = "MediaCas";
97d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang    private ICas mICas;
98d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang    private EventListener mListener;
99d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang    private HandlerThread mHandlerThread;
100d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang    private EventHandler mEventHandler;
101d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang
102d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang    private static final Singleton<IMediaCasService> gDefault =
103d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang            new Singleton<IMediaCasService>() {
104d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        @Override
105d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        protected IMediaCasService create() {
1062659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhang            try {
1072659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhang                return IMediaCasService.getService();
1082659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhang            } catch (RemoteException e) {}
1092659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhang            return null;
110d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        }
111d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang    };
112d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang
113d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang    static IMediaCasService getService() {
114d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        return gDefault.get();
115d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang    }
116d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang
117d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang    private void validateInternalStates() {
118d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        if (mICas == null) {
119d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang            throw new IllegalStateException();
120d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        }
121d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang    }
122d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang
123d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang    private void cleanupAndRethrowIllegalState() {
124d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        mICas = null;
125d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        throw new IllegalStateException();
126d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang    }
127d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang
128d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang    private class EventHandler extends Handler
129d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang    {
130d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        private static final int MSG_CAS_EVENT = 0;
131d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang
132d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        public EventHandler(Looper looper) {
133d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang            super(looper);
134d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        }
135d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang
136d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        @Override
137d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        public void handleMessage(Message msg) {
138d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang            if (msg.what == MSG_CAS_EVENT) {
1392659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhang                mListener.onEvent(MediaCas.this, msg.arg1, msg.arg2,
1402659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhang                        toBytes((ArrayList<Byte>) msg.obj));
141d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang            }
142d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        }
143d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang    }
144d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang
145d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang    private final ICasListener.Stub mBinder = new ICasListener.Stub() {
146d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        @Override
1472659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhang        public void onEvent(int event, int arg, @Nullable ArrayList<Byte> data)
148d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang                throws RemoteException {
149d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang            mEventHandler.sendMessage(mEventHandler.obtainMessage(
150d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang                    EventHandler.MSG_CAS_EVENT, event, arg, data));
151d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        }
152d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang    };
153d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang
154d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang    /**
155d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     * Describe a CAS plugin with its CA_system_ID and string name.
156d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     *
157d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     * Returned as results of {@link #enumeratePlugins}.
158d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     *
159d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     */
160d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang    public static class PluginDescriptor {
161d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        private final int mCASystemId;
162d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        private final String mName;
163d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang
164d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        private PluginDescriptor() {
165d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang            mCASystemId = 0xffff;
166d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang            mName = null;
167d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        }
168d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang
1692659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhang        PluginDescriptor(@NonNull HidlCasPluginDescriptor descriptor) {
1702659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhang            mCASystemId = descriptor.caSystemId;
1712659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhang            mName = descriptor.name;
172d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        }
173d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang
174d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        public int getSystemId() {
175d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang            return mCASystemId;
176d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        }
177d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang
178d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        @NonNull
179d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        public String getName() {
180d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang            return mName;
181d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        }
182d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang
183d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        @Override
184d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        public String toString() {
185d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang            return "PluginDescriptor {" + mCASystemId + ", " + mName + "}";
186d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        }
187d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang    }
188d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang
1892659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhang    private ArrayList<Byte> toByteArray(@NonNull byte[] data, int offset, int length) {
1902659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhang        ArrayList<Byte> byteArray = new ArrayList<Byte>(length);
1912659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhang        for (int i = 0; i < length; i++) {
1922659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhang            byteArray.add(Byte.valueOf(data[offset + i]));
1932659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhang        }
1942659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhang        return byteArray;
1952659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhang    }
1962659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhang
1972659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhang    private ArrayList<Byte> toByteArray(@Nullable byte[] data) {
1982659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhang        if (data == null) {
1992659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhang            return new ArrayList<Byte>();
2002659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhang        }
2012659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhang        return toByteArray(data, 0, data.length);
2022659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhang    }
2032659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhang
2042659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhang    private byte[] toBytes(@NonNull ArrayList<Byte> byteArray) {
2052659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhang        byte[] data = null;
2062659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhang        if (byteArray != null) {
2072659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhang            data = new byte[byteArray.size()];
2082659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhang            for (int i = 0; i < data.length; i++) {
2092659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhang                data[i] = byteArray.get(i);
2102659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhang            }
2112659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhang        }
2122659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhang        return data;
2132659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhang    }
214d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang    /**
215addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang     * Class for an open session with the CA system.
216addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang     */
217addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang    public final class Session implements AutoCloseable {
2182659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhang        final ArrayList<Byte> mSessionId;
219addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang
2202659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhang        Session(@NonNull ArrayList<Byte> sessionId) {
221addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang            mSessionId = sessionId;
222addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang        }
223addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang
224addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang        /**
225addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang         * Set the private data for a session.
226addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang         *
227addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang         * @param data byte array of the private data.
228addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang         *
229addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang         * @throws IllegalStateException if the MediaCas instance is not valid.
230addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang         * @throws MediaCasException for CAS-specific errors.
231addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang         * @throws MediaCasStateException for CAS-specific state exceptions.
232addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang         */
233addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang        public void setPrivateData(@NonNull byte[] data)
234addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang                throws MediaCasException {
235addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang            validateInternalStates();
236addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang
237addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang            try {
2382659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhang                MediaCasException.throwExceptionIfNeeded(
2392659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhang                        mICas.setSessionPrivateData(mSessionId, toByteArray(data, 0, data.length)));
240addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang            } catch (RemoteException e) {
241addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang                cleanupAndRethrowIllegalState();
242addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang            }
243addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang        }
244addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang
245addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang
246addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang        /**
247addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang         * Send a received ECM packet to the specified session of the CA system.
248addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang         *
249addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang         * @param data byte array of the ECM data.
250addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang         * @param offset position within data where the ECM data begins.
251addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang         * @param length length of the data (starting from offset).
252addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang         *
253addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang         * @throws IllegalStateException if the MediaCas instance is not valid.
254addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang         * @throws MediaCasException for CAS-specific errors.
255addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang         * @throws MediaCasStateException for CAS-specific state exceptions.
256addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang         */
257addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang        public void processEcm(@NonNull byte[] data, int offset, int length)
258addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang                throws MediaCasException {
259addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang            validateInternalStates();
260addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang
261addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang            try {
2622659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhang                MediaCasException.throwExceptionIfNeeded(
2632659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhang                        mICas.processEcm(mSessionId, toByteArray(data, offset, length)));
264addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang            } catch (RemoteException e) {
265addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang                cleanupAndRethrowIllegalState();
266addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang            }
267addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang        }
268addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang
269addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang        /**
270addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang         * Send a received ECM packet to the specified session of the CA system.
271addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang         * This is similar to {@link Session#processEcm(byte[], int, int)}
272addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang         * except that the entire byte array is sent.
273addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang         *
274addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang         * @param data byte array of the ECM data.
275addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang         *
276addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang         * @throws IllegalStateException if the MediaCas instance is not valid.
277addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang         * @throws MediaCasException for CAS-specific errors.
278addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang         * @throws MediaCasStateException for CAS-specific state exceptions.
279addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang         */
280addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang        public void processEcm(@NonNull byte[] data) throws MediaCasException {
281addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang            processEcm(data, 0, data.length);
282addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang        }
283addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang
284addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang        /**
285addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang         * Close the session.
286addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang         *
287addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang         * @throws IllegalStateException if the MediaCas instance is not valid.
288addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang         * @throws MediaCasStateException for CAS-specific state exceptions.
289addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang         */
290addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang        @Override
291addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang        public void close() {
292addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang            validateInternalStates();
293addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang
294addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang            try {
2952659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhang                MediaCasStateException.throwExceptionIfNeeded(
2962659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhang                        mICas.closeSession(mSessionId));
297addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang            } catch (RemoteException e) {
298addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang                cleanupAndRethrowIllegalState();
299addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang            }
300addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang        }
301addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang    }
302addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang
3032659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhang    Session createFromSessionId(@NonNull ArrayList<Byte> sessionId) {
3042659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhang        if (sessionId == null || sessionId.size() == 0) {
305addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang            return null;
306addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang        }
307addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang        return new Session(sessionId);
308addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang    }
309addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang
310addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang    /**
311d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     * Query if a certain CA system is supported on this device.
312d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     *
313d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     * @param CA_system_id the id of the CA system.
314d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     *
315d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     * @return Whether the specified CA system is supported on this device.
316d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     */
317d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang    public static boolean isSystemIdSupported(int CA_system_id) {
318d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        IMediaCasService service = getService();
319d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang
320d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        if (service != null) {
321d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang            try {
322d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang                return service.isSystemIdSupported(CA_system_id);
323d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang            } catch (RemoteException e) {
324d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang            }
325d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        }
326d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        return false;
327d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang    }
328d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang
329d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang    /**
330d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     * List all available CA plugins on the device.
331d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     *
332d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     * @return an array of descriptors for the available CA plugins.
333d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     */
334d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang    public static PluginDescriptor[] enumeratePlugins() {
335d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        IMediaCasService service = getService();
336d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang
337d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        if (service != null) {
338d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang            try {
3392659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhang                ArrayList<HidlCasPluginDescriptor> descriptors =
3402659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhang                        service.enumeratePlugins();
3412659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhang                if (descriptors.size() == 0) {
342d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang                    return null;
343d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang                }
3442659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhang                PluginDescriptor[] results = new PluginDescriptor[descriptors.size()];
345d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang                for (int i = 0; i < results.length; i++) {
3462659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhang                    results[i] = new PluginDescriptor(descriptors.get(i));
347d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang                }
348d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang                return results;
349d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang            } catch (RemoteException e) {
350d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang            }
351d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        }
352d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        return null;
353d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang    }
354d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang
355d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang    /**
356d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     * Instantiate a CA system of the specified system id.
357d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     *
358d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     * @param CA_system_id The system id of the CA system.
359d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     *
360d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     * @throws UnsupportedCasException if the device does not support the
361d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     * specified CA system.
362d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     */
363d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang    public MediaCas(int CA_system_id) throws UnsupportedCasException {
364d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        try {
365d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang            mICas = getService().createPlugin(CA_system_id, mBinder);
366d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        } catch(Exception e) {
367d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang            Log.e(TAG, "Failed to create plugin: " + e);
368d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang            mICas = null;
369d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        } finally {
370d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang            if (mICas == null) {
371d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang                throw new UnsupportedCasException(
372d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang                        "Unsupported CA_system_id " + CA_system_id);
373d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang            }
374d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        }
375d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang    }
376d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang
3772659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhang    IHwBinder getBinder() {
378d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        validateInternalStates();
379d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang
380d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        return mICas.asBinder();
381d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang    }
382d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang
383d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang    /**
384d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     * An interface registered by the caller to {@link #setEventListener}
385d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     * to receives scheme-specific notifications from a MediaCas instance.
386d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     */
387d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang    public interface EventListener {
388d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        /**
389d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang         * Notify the listener of a scheme-specific event from the CA system.
390d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang         *
391d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang         * @param MediaCas the MediaCas object to receive this event.
392d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang         * @param event an integer whose meaning is scheme-specific.
393d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang         * @param arg an integer whose meaning is scheme-specific.
394d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang         * @param data a byte array of data whose format and meaning are
395d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang         * scheme-specific.
396d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang         */
397d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        void onEvent(MediaCas MediaCas, int event, int arg, @Nullable byte[] data);
398d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang    }
399d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang
400d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang    /**
401d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     * Set an event listener to receive notifications from the MediaCas instance.
402d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     *
403d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     * @param listener the event listener to be set.
404d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     * @param handler the handler whose looper the event listener will be called on.
405d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     * If handler is null, we'll try to use current thread's looper, or the main
406d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     * looper. If neither are available, an internal thread will be created instead.
407d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     */
408d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang    public void setEventListener(
409d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang            @Nullable EventListener listener, @Nullable Handler handler) {
410d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        mListener = listener;
411d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang
412d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        if (mListener == null) {
413d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang            mEventHandler = null;
414d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang            return;
415d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        }
416d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang
417d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        Looper looper = (handler != null) ? handler.getLooper() : null;
418d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        if (looper == null
419d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang                && (looper = Looper.myLooper()) == null
420d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang                && (looper = Looper.getMainLooper()) == null) {
421d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang            if (mHandlerThread == null || !mHandlerThread.isAlive()) {
422d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang                mHandlerThread = new HandlerThread("MediaCasEventThread",
423d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang                        Process.THREAD_PRIORITY_FOREGROUND);
424d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang                mHandlerThread.start();
425d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang            }
426d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang            looper = mHandlerThread.getLooper();
427d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        }
428d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        mEventHandler = new EventHandler(looper);
429d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang    }
430d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang
431d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang    /**
432d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     * Send the private data for the CA system.
433d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     *
434d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     * @param data byte array of the private data.
435d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     *
436d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     * @throws IllegalStateException if the MediaCas instance is not valid.
437dadee0c33e6cb659978ce4fa6aa21f7c384fc43cChong Zhang     * @throws MediaCasException for CAS-specific errors.
438dadee0c33e6cb659978ce4fa6aa21f7c384fc43cChong Zhang     * @throws MediaCasStateException for CAS-specific state exceptions.
439d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     */
440dadee0c33e6cb659978ce4fa6aa21f7c384fc43cChong Zhang    public void setPrivateData(@NonNull byte[] data) throws MediaCasException {
441d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        validateInternalStates();
442d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang
443d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        try {
4442659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhang            MediaCasException.throwExceptionIfNeeded(
4452659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhang                    mICas.setPrivateData(toByteArray(data, 0, data.length)));
446d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        } catch (RemoteException e) {
447d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang            cleanupAndRethrowIllegalState();
448d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        }
449d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang    }
450d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang
4512659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhang    private class OpenSessionCallback implements ICas.openSessionCallback {
4522659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhang        public Session mSession;
4532659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhang        public int mStatus;
4542659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhang        @Override
4552659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhang        public void onValues(int status, ArrayList<Byte> sessionId) {
4562659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhang            mStatus = status;
4572659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhang            mSession = createFromSessionId(sessionId);
4582659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhang        }
4592659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhang    }
460d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang    /**
461625e1804544d830cab803b165f3d415ed369c436Chong Zhang     * Open a session to descramble one or more streams scrambled by the
462625e1804544d830cab803b165f3d415ed369c436Chong Zhang     * conditional access system.
463d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     *
464addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang     * @return session the newly opened session.
465d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     *
466dadee0c33e6cb659978ce4fa6aa21f7c384fc43cChong Zhang     * @throws IllegalStateException if the MediaCas instance is not valid.
467dadee0c33e6cb659978ce4fa6aa21f7c384fc43cChong Zhang     * @throws MediaCasException for CAS-specific errors.
468dadee0c33e6cb659978ce4fa6aa21f7c384fc43cChong Zhang     * @throws MediaCasStateException for CAS-specific state exceptions.
469d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     */
470625e1804544d830cab803b165f3d415ed369c436Chong Zhang    public Session openSession() throws MediaCasException {
471d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        validateInternalStates();
472d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang
473d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        try {
4742659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhang            OpenSessionCallback cb = new OpenSessionCallback();
4752659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhang            mICas.openSession(cb);
4762659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhang            MediaCasException.throwExceptionIfNeeded(cb.mStatus);
4772659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhang            return cb.mSession;
478d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        } catch (RemoteException e) {
479d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang            cleanupAndRethrowIllegalState();
480d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        }
481d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        return null;
482d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang    }
483d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang
484d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang    /**
485d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     * Send a received EMM packet to the CA system.
486d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     *
487d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     * @param data byte array of the EMM data.
488d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     * @param offset position within data where the EMM data begins.
489d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     * @param length length of the data (starting from offset).
490d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     *
491d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     * @throws IllegalStateException if the MediaCas instance is not valid.
492dadee0c33e6cb659978ce4fa6aa21f7c384fc43cChong Zhang     * @throws MediaCasException for CAS-specific errors.
493dadee0c33e6cb659978ce4fa6aa21f7c384fc43cChong Zhang     * @throws MediaCasStateException for CAS-specific state exceptions.
494d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     */
495dadee0c33e6cb659978ce4fa6aa21f7c384fc43cChong Zhang    public void processEmm(@NonNull byte[] data, int offset, int length)
496dadee0c33e6cb659978ce4fa6aa21f7c384fc43cChong Zhang            throws MediaCasException {
497d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        validateInternalStates();
498d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang
499d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        try {
5002659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhang            MediaCasException.throwExceptionIfNeeded(
5012659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhang                    mICas.processEmm(toByteArray(data, offset, length)));
502d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        } catch (RemoteException e) {
503d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang            cleanupAndRethrowIllegalState();
504d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        }
505d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang    }
506d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang
507d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang    /**
508d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     * Send a received EMM packet to the CA system. This is similar to
509d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     * {@link #processEmm(byte[], int, int)} except that the entire byte
510d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     * array is sent.
511d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     *
512d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     * @param data byte array of the EMM data.
513d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     *
514d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     * @throws IllegalStateException if the MediaCas instance is not valid.
515dadee0c33e6cb659978ce4fa6aa21f7c384fc43cChong Zhang     * @throws MediaCasException for CAS-specific errors.
516dadee0c33e6cb659978ce4fa6aa21f7c384fc43cChong Zhang     * @throws MediaCasStateException for CAS-specific state exceptions.
517d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     */
518dadee0c33e6cb659978ce4fa6aa21f7c384fc43cChong Zhang    public void processEmm(@NonNull byte[] data) throws MediaCasException {
519d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        processEmm(data, 0, data.length);
520d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang    }
521d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang
522d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang    /**
523d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     * Send an event to a CA system. The format of the event is scheme-specific
524d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     * and is opaque to the framework.
525d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     *
526d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     * @param event an integer denoting a scheme-specific event to be sent.
527d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     * @param arg a scheme-specific integer argument for the event.
528d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     * @param data a byte array containing scheme-specific data for the event.
529d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     *
530d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     * @throws IllegalStateException if the MediaCas instance is not valid.
531dadee0c33e6cb659978ce4fa6aa21f7c384fc43cChong Zhang     * @throws MediaCasException for CAS-specific errors.
532dadee0c33e6cb659978ce4fa6aa21f7c384fc43cChong Zhang     * @throws MediaCasStateException for CAS-specific state exceptions.
533d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     */
534dadee0c33e6cb659978ce4fa6aa21f7c384fc43cChong Zhang    public void sendEvent(int event, int arg, @Nullable byte[] data)
535dadee0c33e6cb659978ce4fa6aa21f7c384fc43cChong Zhang            throws MediaCasException {
536d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        validateInternalStates();
537d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang
538d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        try {
5392659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhang            MediaCasException.throwExceptionIfNeeded(
5402659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhang                    mICas.sendEvent(event, arg, toByteArray(data)));
541d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        } catch (RemoteException e) {
542d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang            cleanupAndRethrowIllegalState();
543d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        }
544d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang    }
545d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang
546d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang    /**
547d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     * Initiate a provisioning operation for a CA system.
548d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     *
549d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     * @param provisionString string containing information needed for the
550d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     * provisioning operation, the format of which is scheme and implementation
551d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     * specific.
552d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     *
553d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     * @throws IllegalStateException if the MediaCas instance is not valid.
554dadee0c33e6cb659978ce4fa6aa21f7c384fc43cChong Zhang     * @throws MediaCasException for CAS-specific errors.
555dadee0c33e6cb659978ce4fa6aa21f7c384fc43cChong Zhang     * @throws MediaCasStateException for CAS-specific state exceptions.
556d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     */
557dadee0c33e6cb659978ce4fa6aa21f7c384fc43cChong Zhang    public void provision(@NonNull String provisionString) throws MediaCasException {
558d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        validateInternalStates();
559d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang
560d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        try {
5612659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhang            MediaCasException.throwExceptionIfNeeded(
5622659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhang                    mICas.provision(provisionString));
563d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        } catch (RemoteException e) {
564d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang            cleanupAndRethrowIllegalState();
565d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        }
566d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang    }
567d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang
568d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang    /**
569d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     * Notify the CA system to refresh entitlement keys.
570d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     *
571d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     * @param refreshType the type of the refreshment.
572d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     * @param refreshData private data associated with the refreshment.
573d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     *
574d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     * @throws IllegalStateException if the MediaCas instance is not valid.
575dadee0c33e6cb659978ce4fa6aa21f7c384fc43cChong Zhang     * @throws MediaCasException for CAS-specific errors.
576dadee0c33e6cb659978ce4fa6aa21f7c384fc43cChong Zhang     * @throws MediaCasStateException for CAS-specific state exceptions.
577d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang     */
578dadee0c33e6cb659978ce4fa6aa21f7c384fc43cChong Zhang    public void refreshEntitlements(int refreshType, @Nullable byte[] refreshData)
579dadee0c33e6cb659978ce4fa6aa21f7c384fc43cChong Zhang            throws MediaCasException {
580d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        validateInternalStates();
581d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang
582d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        try {
5832659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhang            MediaCasException.throwExceptionIfNeeded(
5842659c2f16cf55d58f5b3817347340ae84f9eaabdChong Zhang                    mICas.refreshEntitlements(refreshType, toByteArray(refreshData)));
585d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        } catch (RemoteException e) {
586d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang            cleanupAndRethrowIllegalState();
587d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        }
588d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang    }
589d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang
590addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang    @Override
591addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang    public void close() {
592d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        if (mICas != null) {
593d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang            try {
594d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang                mICas.release();
595d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang            } catch (RemoteException e) {
596d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang            } finally {
597d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang                mICas = null;
598d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang            }
599d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang        }
600d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang    }
601d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang
602d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang    @Override
603d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang    protected void finalize() {
604addc39ec27f3eb9b61ad8565804201a866d16abdChong Zhang        close();
605d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang    }
606d5927ae8833780395f8fc5070d0a0a4f8f668292Chong Zhang}