EMMA Coverage Report (generated Fri Aug 23 16:39:17 PDT 2013)
[all classes][org.chromium.media]

COVERAGE SUMMARY FOR SOURCE FILE [MediaDrmBridge.java]

nameclass, %method, %block, %line, %
MediaDrmBridge.java0%   (0/6)0%   (0/35)0%   (0/743)0%   (0/148)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class MediaDrmBridge0%   (0/1)0%   (0/21)0%   (0/484)0%   (0/100)
MediaDrmBridge (UUID, int): void 0%   (0/1)0%   (0/46)0%   (0/11)
access$100 (MediaDrmBridge): int 0%   (0/1)0%   (0/3)0%   (0/1)
access$1000 (MediaDrmBridge, byte []): void 0%   (0/1)0%   (0/4)0%   (0/1)
access$200 (MediaDrmBridge): String 0%   (0/1)0%   (0/3)0%   (0/1)
access$300 (MediaDrmBridge, int, String, byte [], String): void 0%   (0/1)0%   (0/7)0%   (0/1)
access$400 (MediaDrmBridge, int, String): void 0%   (0/1)0%   (0/5)0%   (0/1)
access$500 (MediaDrmBridge, int, String): void 0%   (0/1)0%   (0/5)0%   (0/1)
access$600 (MediaDrmBridge): MediaDrm 0%   (0/1)0%   (0/3)0%   (0/1)
access$700 (MediaDrmBridge): String 0%   (0/1)0%   (0/3)0%   (0/1)
access$800 (MediaDrmBridge, byte [], String): void 0%   (0/1)0%   (0/5)0%   (0/1)
access$900 (MediaDrmBridge): void 0%   (0/1)0%   (0/3)0%   (0/1)
addKey (String, byte []): void 0%   (0/1)0%   (0/72)0%   (0/15)
cancelKeyRequest (String): void 0%   (0/1)0%   (0/32)0%   (0/8)
create (byte [], int): MediaDrmBridge 0%   (0/1)0%   (0/16)0%   (0/4)
generateKeyRequest (byte [], String): void 0%   (0/1)0%   (0/65)0%   (0/15)
getMediaCrypto (): MediaCrypto 0%   (0/1)0%   (0/54)0%   (0/11)
getUUIDFromBytes (byte []): UUID 0%   (0/1)0%   (0/52)0%   (0/9)
onKeyError (): void 0%   (0/1)0%   (0/9)0%   (0/2)
onProvisionResponse (byte []): void 0%   (0/1)0%   (0/19)0%   (0/5)
openSession (): String 0%   (0/1)0%   (0/42)0%   (0/9)
release (): void 0%   (0/1)0%   (0/36)0%   (0/10)
     
class MediaDrmBridge$10%   (0/1)0%   (0/2)0%   (0/25)0%   (0/3)
MediaDrmBridge$1 (MediaDrmBridge, MediaDrm$KeyRequest): void 0%   (0/1)0%   (0/9)0%   (0/1)
run (): void 0%   (0/1)0%   (0/16)0%   (0/2)
     
class MediaDrmBridge$20%   (0/1)0%   (0/2)0%   (0/16)0%   (0/3)
MediaDrmBridge$2 (MediaDrmBridge): void 0%   (0/1)0%   (0/6)0%   (0/1)
run (): void 0%   (0/1)0%   (0/10)0%   (0/2)
     
class MediaDrmBridge$30%   (0/1)0%   (0/2)0%   (0/16)0%   (0/3)
MediaDrmBridge$3 (MediaDrmBridge): void 0%   (0/1)0%   (0/6)0%   (0/1)
run (): void 0%   (0/1)0%   (0/10)0%   (0/2)
     
class MediaDrmBridge$MediaDrmListener0%   (0/1)0%   (0/4)0%   (0/75)0%   (0/14)
<static initializer> 0%   (0/1)0%   (0/8)0%   (0/1)
MediaDrmBridge$MediaDrmListener (MediaDrmBridge): void 0%   (0/1)0%   (0/6)0%   (0/1)
MediaDrmBridge$MediaDrmListener (MediaDrmBridge, MediaDrmBridge$1): void 0%   (0/1)0%   (0/4)0%   (0/1)
onEvent (MediaDrm, byte [], int, int, byte []): void 0%   (0/1)0%   (0/57)0%   (0/13)
     
class MediaDrmBridge$PostRequestTask0%   (0/1)0%   (0/4)0%   (0/127)0%   (0/28)
MediaDrmBridge$PostRequestTask (MediaDrmBridge, byte []): void 0%   (0/1)0%   (0/9)0%   (0/3)
doInBackground (String []): Void 0%   (0/1)0%   (0/27)0%   (0/4)
onPostExecute (Void): void 0%   (0/1)0%   (0/6)0%   (0/2)
postRequest (String, byte []): byte [] 0%   (0/1)0%   (0/85)0%   (0/19)

1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4 
5package org.chromium.media;
6 
7import android.media.MediaCrypto;
8import android.media.MediaDrm;
9import android.os.AsyncTask;
10import android.os.Handler;
11import android.util.Log;
12 
13import org.apache.http.HttpResponse;
14import org.apache.http.client.methods.HttpPost;
15import org.apache.http.client.HttpClient;
16import org.apache.http.client.ClientProtocolException;
17import org.apache.http.impl.client.DefaultHttpClient;
18import org.apache.http.util.EntityUtils;
19import org.chromium.base.CalledByNative;
20import org.chromium.base.JNINamespace;
21 
22import java.io.IOException;
23import java.util.HashMap;
24import java.util.UUID;
25 
26/**
27 * A wrapper of the android MediaDrm class. Each MediaDrmBridge manages multiple
28 * sessions for a single MediaSourcePlayer.
29 */
30@JNINamespace("media")
31class MediaDrmBridge {
32 
33    private static final String TAG = "MediaDrmBridge";
34    private MediaDrm mMediaDrm;
35    private UUID mSchemeUUID;
36    private int mNativeMediaDrmBridge;
37    // TODO(qinmin): we currently only support one session per DRM bridge.
38    // Change this to a HashMap if we start to support multiple sessions.
39    private String mSessionId;
40    private MediaCrypto mMediaCrypto;
41    private String mMimeType;
42    private Handler mhandler;
43 
44    private static UUID getUUIDFromBytes(byte[] data) {
45        if (data.length != 16) {
46            return null;
47        }
48        long mostSigBits = 0;
49        long leastSigBits = 0;
50        for (int i = 0; i < 8; i++) {
51            mostSigBits = (mostSigBits << 8) | (data[i] & 0xff);
52        }
53        for (int i = 8; i < 16; i++) {
54            leastSigBits = (leastSigBits << 8) | (data[i] & 0xff);
55        }
56        return new UUID(mostSigBits, leastSigBits);
57    }
58 
59    private MediaDrmBridge(UUID schemeUUID, int nativeMediaDrmBridge) {
60        try {
61            mSchemeUUID = schemeUUID;
62            mMediaDrm = new MediaDrm(schemeUUID);
63            mNativeMediaDrmBridge = nativeMediaDrmBridge;
64            mMediaDrm.setOnEventListener(new MediaDrmListener());
65            mSessionId = openSession();
66            mhandler = new Handler();
67        } catch (android.media.UnsupportedSchemeException e) {
68            Log.e(TAG, "Unsupported DRM scheme " + e.toString());
69        }
70    }
71 
72    /**
73     * Open a new session and return the sessionId.
74     *
75     * @return ID of the session.
76     */
77    private String openSession() {
78        String session = null;
79        try {
80            final byte[] sessionId = mMediaDrm.openSession();
81            session = new String(sessionId, "UTF-8");
82        } catch (android.media.NotProvisionedException e) {
83            Log.e(TAG, "Cannot open a new session " + e.toString());
84        } catch (java.io.UnsupportedEncodingException e) {
85            Log.e(TAG, "Cannot open a new session " + e.toString());
86        }
87        return session;
88    }
89 
90    /**
91     * Create a new MediaDrmBridge from the crypto scheme UUID.
92     *
93     * @param schemeUUID Crypto scheme UUID.
94     * @param nativeMediaDrmBridge Native object of this class.
95     */
96    @CalledByNative
97    private static MediaDrmBridge create(byte[] schemeUUID, int nativeMediaDrmBridge) {
98        UUID cryptoScheme = getUUIDFromBytes(schemeUUID);
99        if (cryptoScheme != null && MediaDrm.isCryptoSchemeSupported(cryptoScheme)) {
100            return new MediaDrmBridge(cryptoScheme, nativeMediaDrmBridge);
101        }
102        return null;
103    }
104 
105    /**
106     * Create a new MediaCrypto object from the session Id.
107     *
108     * @param sessionId Crypto session Id.
109     */
110    @CalledByNative
111    private MediaCrypto getMediaCrypto() {
112        if (mMediaCrypto != null) {
113            return mMediaCrypto;
114        }
115        try {
116            final byte[] session = mSessionId.getBytes("UTF-8");
117            if (MediaCrypto.isCryptoSchemeSupported(mSchemeUUID)) {
118                mMediaCrypto = new MediaCrypto(mSchemeUUID, session);
119            }
120        } catch (android.media.MediaCryptoException e) {
121            Log.e(TAG, "Cannot create MediaCrypto " + e.toString());
122        } catch (java.io.UnsupportedEncodingException e) {
123            Log.e(TAG, "Cannot create MediaCrypto " + e.toString());
124        }
125        return mMediaCrypto;
126    }
127 
128    /**
129     * Release the MediaDrmBridge object.
130     */
131    @CalledByNative
132    private void release() {
133        if (mMediaCrypto != null) {
134            mMediaCrypto.release();
135        }
136        if (mSessionId != null) {
137            try {
138                final byte[] session = mSessionId.getBytes("UTF-8");
139                mMediaDrm.closeSession(session);
140            } catch (java.io.UnsupportedEncodingException e) {
141                Log.e(TAG, "Failed to close session " + e.toString());
142            }
143        }
144        mMediaDrm.release();
145    }
146 
147    /**
148     * Generate a key request and post an asynchronous task to the native side
149     * with the response message.
150     *
151     * @param initData Data needed to generate the key request.
152     * @param mime Mime type.
153     */
154    @CalledByNative
155    private void generateKeyRequest(byte[] initData, String mime) {
156        if (mSessionId == null) {
157            return;
158        }
159        try {
160            final byte[] session = mSessionId.getBytes("UTF-8");
161            mMimeType = mime;
162            HashMap<String, String> optionalParameters = new HashMap<String, String>();
163            final MediaDrm.KeyRequest request = mMediaDrm.getKeyRequest(
164                    session, initData, mime, MediaDrm.KEY_TYPE_STREAMING, optionalParameters);
165            mhandler.post(new Runnable(){
166                public void run() {
167                    nativeOnKeyMessage(mNativeMediaDrmBridge, mSessionId,
168                            request.getData(), request.getDefaultUrl());
169                }
170            });
171            return;
172        } catch (android.media.NotProvisionedException e) {
173            Log.e(TAG, "Cannot get key request " + e.toString());
174        } catch (java.io.UnsupportedEncodingException e) {
175            Log.e(TAG, "Cannot get key request " + e.toString());
176        }
177        onKeyError();
178    }
179 
180    /**
181     * Cancel a key request for a session Id.
182     *
183     * @param sessionId Crypto session Id.
184     */
185    @CalledByNative
186    private void cancelKeyRequest(String sessionId) {
187        if (mSessionId == null || !mSessionId.equals(sessionId)) {
188            return;
189        }
190        try {
191            final byte[] session = sessionId.getBytes("UTF-8");
192            mMediaDrm.removeKeys(session);
193        } catch (java.io.UnsupportedEncodingException e) {
194            Log.e(TAG, "Cannot cancel key request " + e.toString());
195        }
196    }
197 
198    /**
199     * Add a key for a session Id.
200     *
201     * @param sessionId Crypto session Id.
202     * @param key Response data from the server.
203     */
204    @CalledByNative
205    private void addKey(String sessionId, byte[] key) {
206        if (mSessionId == null || !mSessionId.equals(sessionId)) {
207            return;
208        }
209        try {
210            final byte[] session = sessionId.getBytes("UTF-8");
211            mMediaDrm.provideKeyResponse(session, key);
212            mhandler.post(new Runnable() {
213                public void run() {
214                    nativeOnKeyAdded(mNativeMediaDrmBridge, mSessionId);
215                }
216            });
217            return;
218        } catch (android.media.NotProvisionedException e) {
219            Log.e(TAG, "failed to provide key response " + e.toString());
220        } catch (android.media.DeniedByServerException e) {
221            Log.e(TAG, "failed to provide key response " + e.toString());
222        } catch (java.io.UnsupportedEncodingException e) {
223            Log.e(TAG, "failed to provide key response " + e.toString());
224        }
225        onKeyError();
226    }
227 
228    /**
229     * Called when the provision response is received.
230     *
231     * @param response Response data from the provision server.
232     */
233    private void onProvisionResponse(byte[] response) {
234        try {
235            mMediaDrm.provideProvisionResponse(response);
236        } catch (android.media.DeniedByServerException e) {
237            Log.e(TAG, "failed to provide key response " + e.toString());
238        }
239    }
240 
241    private void onKeyError() {
242        // TODO(qinmin): pass the error code to native.
243        mhandler.post(new Runnable() {
244            public void run() {
245                nativeOnKeyError(mNativeMediaDrmBridge, mSessionId);
246            }
247        });
248    }
249 
250    private class MediaDrmListener implements MediaDrm.OnEventListener {
251        @Override
252        public void onEvent(MediaDrm mediaDrm, byte[] sessionId, int event, int extra,
253                byte[] data) {
254            switch(event) {
255                case MediaDrm.EVENT_PROVISION_REQUIRED:
256                    MediaDrm.ProvisionRequest request = mMediaDrm.getProvisionRequest();
257                    PostRequestTask postTask = new PostRequestTask(request.getData());
258                    postTask.execute(request.getDefaultUrl());
259                    break;
260                case MediaDrm.EVENT_KEY_REQUIRED:
261                    generateKeyRequest(data, mMimeType);
262                    break;
263                case MediaDrm.EVENT_KEY_EXPIRED:
264                    onKeyError();
265                    break;
266                case MediaDrm.EVENT_VENDOR_DEFINED:
267                    assert(false);
268                    break;
269                default:
270                    Log.e(TAG, "Invalid DRM event " + (int)event);
271                    return;
272            }
273        }
274    }
275 
276    private class PostRequestTask extends AsyncTask<String, Void, Void> {
277        private static final String TAG = "PostRequestTask";
278 
279        private byte[] mDrmRequest;
280        private byte[] mResponseBody;
281 
282        public PostRequestTask(byte[] drmRequest) {
283            mDrmRequest = drmRequest;
284        }
285 
286        @Override
287        protected Void doInBackground(String... urls) {
288            mResponseBody = postRequest(urls[0], mDrmRequest);
289            if (mResponseBody != null) {
290                Log.d(TAG, "response length=" + mResponseBody.length);
291            }
292            return null;
293        }
294 
295        private byte[] postRequest(String url, byte[] drmRequest) {
296            HttpClient httpClient = new DefaultHttpClient();
297            HttpPost httpPost = new HttpPost(url + "&signedRequest=" + new String(drmRequest));
298 
299            Log.d(TAG, "PostRequest:" + httpPost.getRequestLine());
300            try {
301                // Add data
302                httpPost.setHeader("Accept", "*/*");
303                httpPost.setHeader("User-Agent", "Widevine CDM v1.0");
304                httpPost.setHeader("Content-Type", "application/json");
305 
306                // Execute HTTP Post Request
307                HttpResponse response = httpClient.execute(httpPost);
308 
309                byte[] responseBody;
310                int responseCode = response.getStatusLine().getStatusCode();
311                if (responseCode == 200) {
312                    responseBody = EntityUtils.toByteArray(response.getEntity());
313                } else {
314                    Log.d(TAG, "Server returned HTTP error code " + responseCode);
315                    return null;
316                }
317                return responseBody;
318            } catch (ClientProtocolException e) {
319                e.printStackTrace();
320            } catch (IOException e) {
321                e.printStackTrace();
322            }
323            return null;
324        }
325 
326        @Override
327        protected void onPostExecute(Void v) {
328            onProvisionResponse(mResponseBody);
329        }
330    }
331 
332    private native void nativeOnKeyMessage(int nativeMediaDrmBridge, String sessionId,
333                                           byte[] message, String destinationUrl);
334 
335    private native void nativeOnKeyAdded(int nativeMediaDrmBridge, String sessionId);
336 
337    private native void nativeOnKeyError(int nativeMediaDrmBridge, String sessionId);
338}

[all classes][org.chromium.media]
EMMA 2.0.5312 (C) Vladimir Roubtsov