MockDrmCryptoPlugin.cpp revision 4c63a239c404af1e055e5f9939939ab0fd09d98a
1/*
2 * Copyright (C) 2013 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//#define LOG_NDEBUG 0
18#define LOG_TAG "MockDrmCryptoPlugin"
19#include <utils/Log.h>
20
21
22#include "drm/DrmAPI.h"
23#include "MockDrmCryptoPlugin.h"
24#include "media/stagefright/MediaErrors.h"
25
26using namespace android;
27
28// Shared library entry point
29DrmFactory *createDrmFactory()
30{
31    return new MockDrmFactory();
32}
33
34// Shared library entry point
35CryptoFactory *createCryptoFactory()
36{
37    return new MockCryptoFactory();
38}
39
40const uint8_t mock_uuid[16] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
41                               0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10};
42
43namespace android {
44
45    // MockDrmFactory
46    bool MockDrmFactory::isCryptoSchemeSupported(const uint8_t uuid[16])
47    {
48        return (!memcmp(uuid, mock_uuid, sizeof(uuid)));
49    }
50
51    status_t MockDrmFactory::createDrmPlugin(const uint8_t uuid[16], DrmPlugin **plugin)
52    {
53        *plugin = new MockDrmPlugin();
54        return OK;
55    }
56
57    // MockCryptoFactory
58    bool MockCryptoFactory::isCryptoSchemeSupported(const uint8_t uuid[16]) const
59    {
60        return (!memcmp(uuid, mock_uuid, sizeof(uuid)));
61    }
62
63    status_t MockCryptoFactory::createPlugin(const uint8_t uuid[16], const void *data,
64                                             size_t size, CryptoPlugin **plugin)
65    {
66        *plugin = new MockCryptoPlugin();
67        return OK;
68    }
69
70
71    // MockDrmPlugin methods
72
73    status_t MockDrmPlugin::openSession(Vector<uint8_t> &sessionId)
74    {
75        const size_t kSessionIdSize = 8;
76
77        Mutex::Autolock lock(mLock);
78        for (size_t i = 0; i < kSessionIdSize / sizeof(long); i++) {
79            long r = random();
80            sessionId.appendArray((uint8_t *)&r, sizeof(long));
81        }
82        mSessions.add(sessionId);
83
84        ALOGD("MockDrmPlugin::openSession() -> %s", vectorToString(sessionId).string());
85        return OK;
86    }
87
88    status_t MockDrmPlugin::closeSession(Vector<uint8_t> const &sessionId)
89    {
90        Mutex::Autolock lock(mLock);
91        ALOGD("MockDrmPlugin::closeSession(%s)", vectorToString(sessionId).string());
92        ssize_t index = findSession(sessionId);
93        if (index == kNotFound) {
94            ALOGD("Invalid sessionId");
95            return BAD_VALUE;
96        }
97        mSessions.removeAt(index);
98        return OK;
99    }
100
101
102    status_t MockDrmPlugin::getKeyRequest(Vector<uint8_t> const &sessionId,
103                                          Vector<uint8_t> const &initData,
104                                          String8 const &mimeType, KeyType keyType,
105                                          KeyedVector<String8, String8> const &optionalParameters,
106                                          Vector<uint8_t> &request, String8 &defaultUrl)
107    {
108        Mutex::Autolock lock(mLock);
109        ALOGD("MockDrmPlugin::getKeyRequest(sessionId=%s, initData=%s, mimeType=%s"
110              ", keyType=%d, optionalParameters=%s))",
111              vectorToString(sessionId).string(), vectorToString(initData).string(), mimeType.string(),
112              keyType, stringMapToString(optionalParameters).string());
113
114        ssize_t index = findSession(sessionId);
115        if (index == kNotFound) {
116            ALOGD("Invalid sessionId");
117            return BAD_VALUE;
118        }
119
120        // Properties used in mock test, set by mock plugin and verifed cts test app
121        //   byte[] initData           -> mock-initdata
122        //   string mimeType           -> mock-mimetype
123        //   string keyType            -> mock-keytype
124        //   string optionalParameters -> mock-optparams formatted as {key1,value1},{key2,value2}
125
126        mByteArrayProperties.add(String8("mock-initdata"), initData);
127        mStringProperties.add(String8("mock-mimetype"), mimeType);
128
129        String8 keyTypeStr;
130        keyTypeStr.appendFormat("%d", (int)keyType);
131        mStringProperties.add(String8("mock-keytype"), keyTypeStr);
132
133        String8 params;
134        for (size_t i = 0; i < optionalParameters.size(); i++) {
135            params.appendFormat("%s{%s,%s}", i ? "," : "",
136                                optionalParameters.keyAt(i).string(),
137                                optionalParameters.valueAt(i).string());
138        }
139        mStringProperties.add(String8("mock-optparams"), params);
140
141        // Properties used in mock test, set by cts test app returned from mock plugin
142        //   byte[] mock-request       -> request
143        //   string mock-default-url   -> defaultUrl
144
145        index = mByteArrayProperties.indexOfKey(String8("mock-request"));
146        if (index < 0) {
147            ALOGD("Missing 'mock-request' parameter for mock");
148            return BAD_VALUE;
149        } else {
150            request = mByteArrayProperties.valueAt(index);
151        }
152
153        index = mStringProperties.indexOfKey(String8("mock-defaultUrl"));
154        if (index < 0) {
155            ALOGD("Missing 'mock-defaultUrl' parameter for mock");
156            return BAD_VALUE;
157        } else {
158            defaultUrl = mStringProperties.valueAt(index);
159        }
160        return OK;
161    }
162
163    status_t MockDrmPlugin::provideKeyResponse(Vector<uint8_t> const &sessionId,
164                                               Vector<uint8_t> const &response,
165                                               Vector<uint8_t> &keySetId)
166    {
167        Mutex::Autolock lock(mLock);
168        ALOGD("MockDrmPlugin::provideKeyResponse(sessionId=%s, response=%s)",
169              vectorToString(sessionId).string(), vectorToString(response).string());
170        ssize_t index = findSession(sessionId);
171        if (index == kNotFound) {
172            ALOGD("Invalid sessionId");
173            return BAD_VALUE;
174        }
175        if (response.size() == 0) {
176            return BAD_VALUE;
177        }
178
179        // Properties used in mock test, set by mock plugin and verifed cts test app
180        //   byte[] response            -> mock-response
181        mByteArrayProperties.add(String8("mock-response"), response);
182
183        const size_t kKeySetIdSize = 8;
184
185        for (size_t i = 0; i < kKeySetIdSize / sizeof(long); i++) {
186            long r = random();
187            keySetId.appendArray((uint8_t *)&r, sizeof(long));
188        }
189        mKeySets.add(keySetId);
190
191        return OK;
192    }
193
194    status_t MockDrmPlugin::removeKeys(Vector<uint8_t> const &keySetId)
195    {
196        Mutex::Autolock lock(mLock);
197        ALOGD("MockDrmPlugin::removeKeys(keySetId=%s)",
198              vectorToString(keySetId).string());
199
200        ssize_t index = findKeySet(keySetId);
201        if (index == kNotFound) {
202            ALOGD("Invalid keySetId");
203            return BAD_VALUE;
204        }
205        mKeySets.removeAt(index);
206
207        return OK;
208    }
209
210    status_t MockDrmPlugin::restoreKeys(Vector<uint8_t> const &sessionId,
211                                        Vector<uint8_t> const &keySetId)
212    {
213        Mutex::Autolock lock(mLock);
214        ALOGD("MockDrmPlugin::restoreKeys(sessionId=%s, keySetId=%s)",
215              vectorToString(sessionId).string(),
216              vectorToString(keySetId).string());
217        ssize_t index = findSession(sessionId);
218        if (index == kNotFound) {
219            ALOGD("Invalid sessionId");
220            return BAD_VALUE;
221        }
222
223        index = findKeySet(keySetId);
224        if (index == kNotFound) {
225            ALOGD("Invalid keySetId");
226            return BAD_VALUE;
227        }
228
229        return OK;
230    }
231
232    status_t MockDrmPlugin::queryKeyStatus(Vector<uint8_t> const &sessionId,
233                                               KeyedVector<String8, String8> &infoMap) const
234    {
235        ALOGD("MockDrmPlugin::queryKeyStatus(sessionId=%s)",
236              vectorToString(sessionId).string());
237
238        ssize_t index = findSession(sessionId);
239        if (index == kNotFound) {
240            ALOGD("Invalid sessionId");
241            return BAD_VALUE;
242        }
243
244        infoMap.add(String8("purchaseDuration"), String8("1000"));
245        infoMap.add(String8("licenseDuration"), String8("100"));
246        return OK;
247    }
248
249    status_t MockDrmPlugin::getProvisionRequest(Vector<uint8_t> &request,
250                                                String8 &defaultUrl)
251    {
252        Mutex::Autolock lock(mLock);
253        ALOGD("MockDrmPlugin::getProvisionRequest()");
254
255        // Properties used in mock test, set by cts test app returned from mock plugin
256        //   byte[] mock-request       -> request
257        //   string mock-default-url   -> defaultUrl
258
259        ssize_t index = mByteArrayProperties.indexOfKey(String8("mock-request"));
260        if (index < 0) {
261            ALOGD("Missing 'mock-request' parameter for mock");
262            return BAD_VALUE;
263        } else {
264            request = mByteArrayProperties.valueAt(index);
265        }
266
267        index = mStringProperties.indexOfKey(String8("mock-defaultUrl"));
268        if (index < 0) {
269            ALOGD("Missing 'mock-defaultUrl' parameter for mock");
270            return BAD_VALUE;
271        } else {
272            defaultUrl = mStringProperties.valueAt(index);
273        }
274        return OK;
275    }
276
277    status_t MockDrmPlugin::provideProvisionResponse(Vector<uint8_t> const &response)
278    {
279        Mutex::Autolock lock(mLock);
280        ALOGD("MockDrmPlugin::provideProvisionResponse(%s)",
281              vectorToString(response).string());
282
283        // Properties used in mock test, set by mock plugin and verifed cts test app
284        //   byte[] response            -> mock-response
285
286        mByteArrayProperties.add(String8("mock-response"), response);
287        return OK;
288    }
289
290    status_t MockDrmPlugin::getSecureStops(List<Vector<uint8_t> > &secureStops)
291    {
292        Mutex::Autolock lock(mLock);
293        ALOGD("MockDrmPlugin::getSecureStops()");
294        const uint8_t ss1[] = {0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89};
295        const uint8_t ss2[] = {0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99};
296
297        Vector<uint8_t> vec;
298        vec.appendArray(ss1, sizeof(ss1));
299        secureStops.push_back(vec);
300
301        vec.clear();
302        vec.appendArray(ss2, sizeof(ss2));
303        secureStops.push_back(vec);
304        return OK;
305    }
306
307    status_t MockDrmPlugin::releaseSecureStops(Vector<uint8_t> const &ssRelease)
308    {
309        Mutex::Autolock lock(mLock);
310        ALOGD("MockDrmPlugin::releaseSecureStops(%s)",
311              vectorToString(ssRelease).string());
312        return OK;
313    }
314
315    status_t MockDrmPlugin::getPropertyString(String8 const &name, String8 &value) const
316    {
317        ALOGD("MockDrmPlugin::getPropertyString(name=%s)", name.string());
318        ssize_t index = mStringProperties.indexOfKey(name);
319        if (index < 0) {
320            ALOGD("no property for '%s'", name.string());
321            return BAD_VALUE;
322        }
323        value = mStringProperties.valueAt(index);
324        return OK;
325    }
326
327    status_t MockDrmPlugin::getPropertyByteArray(String8 const &name,
328                                                 Vector<uint8_t> &value) const
329    {
330        ALOGD("MockDrmPlugin::getPropertyByteArray(name=%s)", name.string());
331        ssize_t index = mByteArrayProperties.indexOfKey(name);
332        if (index < 0) {
333            ALOGD("no property for '%s'", name.string());
334            return BAD_VALUE;
335        }
336        value = mByteArrayProperties.valueAt(index);
337        return OK;
338    }
339
340    status_t MockDrmPlugin::setPropertyString(String8 const &name,
341                                              String8 const &value)
342    {
343        Mutex::Autolock lock(mLock);
344        ALOGD("MockDrmPlugin::setPropertyString(name=%s, value=%s)",
345              name.string(), value.string());
346        mStringProperties.add(name, value);
347        return OK;
348    }
349
350    status_t MockDrmPlugin::setPropertyByteArray(String8 const &name,
351                                                 Vector<uint8_t> const &value)
352    {
353        Mutex::Autolock lock(mLock);
354        ALOGD("MockDrmPlugin::setPropertyByteArray(name=%s, value=%s)",
355              name.string(), vectorToString(value).string());
356        mByteArrayProperties.add(name, value);
357        return OK;
358    }
359
360    status_t MockDrmPlugin::setCipherAlgorithm(Vector<uint8_t> const &sessionId,
361                                               String8 const &algorithm)
362    {
363        Mutex::Autolock lock(mLock);
364
365        ALOGD("MockDrmPlugin::setCipherAlgorithm(sessionId=%s, algorithm=%s)",
366              vectorToString(sessionId).string(), algorithm.string());
367
368        ssize_t index = findSession(sessionId);
369        if (index == kNotFound) {
370            ALOGD("Invalid sessionId");
371            return BAD_VALUE;
372        }
373
374        if (algorithm == "AES/CBC/NoPadding") {
375            return OK;
376        }
377        return BAD_VALUE;
378    }
379
380    status_t MockDrmPlugin::setMacAlgorithm(Vector<uint8_t> const &sessionId,
381                                            String8 const &algorithm)
382    {
383        Mutex::Autolock lock(mLock);
384
385        ALOGD("MockDrmPlugin::setMacAlgorithm(sessionId=%s, algorithm=%s)",
386              vectorToString(sessionId).string(), algorithm.string());
387
388        ssize_t index = findSession(sessionId);
389        if (index == kNotFound) {
390            ALOGD("Invalid sessionId");
391            return BAD_VALUE;
392        }
393
394        if (algorithm == "HmacSHA256") {
395            return OK;
396        }
397        return BAD_VALUE;
398    }
399
400    status_t MockDrmPlugin::encrypt(Vector<uint8_t> const &sessionId,
401                                    Vector<uint8_t> const &keyId,
402                                    Vector<uint8_t> const &input,
403                                    Vector<uint8_t> const &iv,
404                                    Vector<uint8_t> &output)
405    {
406        Mutex::Autolock lock(mLock);
407        ALOGD("MockDrmPlugin::encrypt(sessionId=%s, keyId=%s, input=%s, iv=%s)",
408              vectorToString(sessionId).string(),
409              vectorToString(keyId).string(),
410              vectorToString(input).string(),
411              vectorToString(iv).string());
412
413        ssize_t index = findSession(sessionId);
414        if (index == kNotFound) {
415            ALOGD("Invalid sessionId");
416            return BAD_VALUE;
417        }
418
419        // Properties used in mock test, set by mock plugin and verifed cts test app
420        //   byte[] keyId              -> mock-keyid
421        //   byte[] input              -> mock-input
422        //   byte[] iv                 -> mock-iv
423        mByteArrayProperties.add(String8("mock-keyid"), keyId);
424        mByteArrayProperties.add(String8("mock-input"), input);
425        mByteArrayProperties.add(String8("mock-iv"), iv);
426
427        // Properties used in mock test, set by cts test app returned from mock plugin
428        //   byte[] mock-output        -> output
429        index = mByteArrayProperties.indexOfKey(String8("mock-output"));
430        if (index < 0) {
431            ALOGD("Missing 'mock-request' parameter for mock");
432            return BAD_VALUE;
433        } else {
434            output = mByteArrayProperties.valueAt(index);
435        }
436        return OK;
437    }
438
439    status_t MockDrmPlugin::decrypt(Vector<uint8_t> const &sessionId,
440                                    Vector<uint8_t> const &keyId,
441                                    Vector<uint8_t> const &input,
442                                    Vector<uint8_t> const &iv,
443                                    Vector<uint8_t> &output)
444    {
445        Mutex::Autolock lock(mLock);
446        ALOGD("MockDrmPlugin::decrypt(sessionId=%s, keyId=%s, input=%s, iv=%s)",
447              vectorToString(sessionId).string(),
448              vectorToString(keyId).string(),
449              vectorToString(input).string(),
450              vectorToString(iv).string());
451
452        ssize_t index = findSession(sessionId);
453        if (index == kNotFound) {
454            ALOGD("Invalid sessionId");
455            return BAD_VALUE;
456        }
457
458        // Properties used in mock test, set by mock plugin and verifed cts test app
459        //   byte[] keyId              -> mock-keyid
460        //   byte[] input              -> mock-input
461        //   byte[] iv                 -> mock-iv
462        mByteArrayProperties.add(String8("mock-keyid"), keyId);
463        mByteArrayProperties.add(String8("mock-input"), input);
464        mByteArrayProperties.add(String8("mock-iv"), iv);
465
466        // Properties used in mock test, set by cts test app returned from mock plugin
467        //   byte[] mock-output        -> output
468        index = mByteArrayProperties.indexOfKey(String8("mock-output"));
469        if (index < 0) {
470            ALOGD("Missing 'mock-request' parameter for mock");
471            return BAD_VALUE;
472        } else {
473            output = mByteArrayProperties.valueAt(index);
474        }
475        return OK;
476    }
477
478    status_t MockDrmPlugin::sign(Vector<uint8_t> const &sessionId,
479                                 Vector<uint8_t> const &keyId,
480                                 Vector<uint8_t> const &message,
481                                 Vector<uint8_t> &signature)
482    {
483        Mutex::Autolock lock(mLock);
484        ALOGD("MockDrmPlugin::sign(sessionId=%s, keyId=%s, message=%s)",
485              vectorToString(sessionId).string(),
486              vectorToString(keyId).string(),
487              vectorToString(message).string());
488
489        ssize_t index = findSession(sessionId);
490        if (index == kNotFound) {
491            ALOGD("Invalid sessionId");
492            return BAD_VALUE;
493        }
494
495        // Properties used in mock test, set by mock plugin and verifed cts test app
496        //   byte[] keyId              -> mock-keyid
497        //   byte[] message            -> mock-message
498        mByteArrayProperties.add(String8("mock-keyid"), keyId);
499        mByteArrayProperties.add(String8("mock-message"), message);
500
501        // Properties used in mock test, set by cts test app returned from mock plugin
502        //   byte[] mock-signature        -> signature
503        index = mByteArrayProperties.indexOfKey(String8("mock-signature"));
504        if (index < 0) {
505            ALOGD("Missing 'mock-request' parameter for mock");
506            return BAD_VALUE;
507        } else {
508            signature = mByteArrayProperties.valueAt(index);
509        }
510        return OK;
511    }
512
513    status_t MockDrmPlugin::verify(Vector<uint8_t> const &sessionId,
514                                   Vector<uint8_t> const &keyId,
515                                   Vector<uint8_t> const &message,
516                                   Vector<uint8_t> const &signature,
517                                   bool &match)
518    {
519        Mutex::Autolock lock(mLock);
520        ALOGD("MockDrmPlugin::verify(sessionId=%s, keyId=%s, message=%s, signature=%s)",
521              vectorToString(sessionId).string(),
522              vectorToString(keyId).string(),
523              vectorToString(message).string(),
524              vectorToString(signature).string());
525
526        ssize_t index = findSession(sessionId);
527        if (index == kNotFound) {
528            ALOGD("Invalid sessionId");
529            return BAD_VALUE;
530        }
531
532        // Properties used in mock test, set by mock plugin and verifed cts test app
533        //   byte[] keyId              -> mock-keyid
534        //   byte[] message            -> mock-message
535        //   byte[] signature          -> mock-signature
536        mByteArrayProperties.add(String8("mock-keyid"), keyId);
537        mByteArrayProperties.add(String8("mock-message"), message);
538        mByteArrayProperties.add(String8("mock-signature"), signature);
539
540        // Properties used in mock test, set by cts test app returned from mock plugin
541        //   String mock-match "1" or "0"         -> match
542        index = mStringProperties.indexOfKey(String8("mock-match"));
543        if (index < 0) {
544            ALOGD("Missing 'mock-request' parameter for mock");
545            return BAD_VALUE;
546        } else {
547            match = atol(mStringProperties.valueAt(index).string());
548        }
549        return OK;
550    }
551
552    ssize_t MockDrmPlugin::findSession(Vector<uint8_t> const &sessionId) const
553    {
554        ALOGD("findSession: nsessions=%d, size=%d", mSessions.size(), sessionId.size());
555        for (size_t i = 0; i < mSessions.size(); ++i) {
556            if (memcmp(mSessions[i].array(), sessionId.array(), sessionId.size()) == 0) {
557                return i;
558            }
559        }
560        return kNotFound;
561    }
562
563    ssize_t MockDrmPlugin::findKeySet(Vector<uint8_t> const &keySetId) const
564    {
565        ALOGD("findKeySet: nkeySets=%d, size=%d", mKeySets.size(), keySetId.size());
566        for (size_t i = 0; i < mKeySets.size(); ++i) {
567            if (memcmp(mKeySets[i].array(), keySetId.array(), keySetId.size()) == 0) {
568                return i;
569            }
570        }
571        return kNotFound;
572    }
573
574
575    // Conversion utilities
576    String8 MockDrmPlugin::vectorToString(Vector<uint8_t> const &vector) const
577    {
578        return arrayToString(vector.array(), vector.size());
579    }
580
581    String8 MockDrmPlugin::arrayToString(uint8_t const *array, size_t len) const
582    {
583        String8 result("{ ");
584        for (size_t i = 0; i < len; i++) {
585            result.appendFormat("0x%02x ", array[i]);
586        }
587        result += "}";
588        return result;
589    }
590
591    String8 MockDrmPlugin::stringMapToString(KeyedVector<String8, String8> map) const
592    {
593        String8 result("{ ");
594        for (size_t i = 0; i < map.size(); i++) {
595            result.appendFormat("%s{name=%s, value=%s}", i > 0 ? ", " : "",
596                                map.keyAt(i).string(), map.valueAt(i).string());
597        }
598        return result + " }";
599    }
600
601    bool operator<(Vector<uint8_t> const &lhs, Vector<uint8_t> const &rhs) {
602        return lhs.size() < rhs.size() || (memcmp(lhs.array(), rhs.array(), lhs.size()) < 0);
603    }
604
605    //
606    // Crypto Plugin
607    //
608
609    bool MockCryptoPlugin::requiresSecureDecoderComponent(const char *mime) const
610    {
611        ALOGD("MockCryptoPlugin::requiresSecureDecoderComponent(mime=%s)", mime);
612        return false;
613    }
614
615    ssize_t
616    MockCryptoPlugin::decrypt(bool secure, const uint8_t key[16], const uint8_t iv[16],
617                              Mode mode, const void *srcPtr, const SubSample *subSamples,
618                              size_t numSubSamples, void *dstPtr, AString *errorDetailMsg)
619    {
620        ALOGD("MockCryptoPlugin::decrypt(secure=%d, key=%s, iv=%s, mode=%d, src=%p, "
621              "subSamples=%s, dst=%p)",
622              (int)secure,
623              arrayToString(key, sizeof(key)).string(),
624              arrayToString(iv, sizeof(iv)).string(),
625              (int)mode, srcPtr,
626              subSamplesToString(subSamples, numSubSamples).string(),
627              dstPtr);
628        return OK;
629    }
630
631    // Conversion utilities
632    String8 MockCryptoPlugin::arrayToString(uint8_t const *array, size_t len) const
633    {
634        String8 result("{ ");
635        for (size_t i = 0; i < len; i++) {
636            result.appendFormat("0x%02x ", array[i]);
637        }
638        result += "}";
639        return result;
640    }
641
642    String8 MockCryptoPlugin::subSamplesToString(SubSample const *subSamples,
643                                                 size_t numSubSamples) const
644    {
645        String8 result;
646        for (size_t i = 0; i < numSubSamples; i++) {
647            result.appendFormat("[%d] {clear:%d, encrypted:%d} ", i,
648                                subSamples[i].mNumBytesOfClearData,
649                                subSamples[i].mNumBytesOfEncryptedData);
650        }
651        return result;
652    }
653
654};
655