1 /*
2  * Copyright (C) 2019 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 #include "vendor_modules.h"
18 #define LOG_TAG "[email protected]"
19 
20 #include <android/hidl/allocator/1.0/IAllocator.h>
21 #include <gtest/gtest.h>
22 #include <hidl/HidlSupport.h>
23 #include <hidlmemory/mapping.h>
24 #include <log/log.h>
25 #include <openssl/aes.h>
26 #include <random>
27 
28 #include "drm_hal_clearkey_module.h"
29 #include "android/hardware/drm/1.2/vts/drm_hal_common.h"
30 
31 using ::android::hardware::drm::V1_0::BufferType;
32 using ::android::hardware::drm::V1_0::DestinationBuffer;
33 using ICryptoPluginV1_0 = ::android::hardware::drm::V1_0::ICryptoPlugin;
34 using IDrmPluginV1_0 = ::android::hardware::drm::V1_0::IDrmPlugin;
35 using ::android::hardware::drm::V1_0::KeyValue;
36 using ::android::hardware::drm::V1_0::SharedBuffer;
37 using StatusV1_0 = ::android::hardware::drm::V1_0::Status;
38 
39 using ::android::hardware::drm::V1_1::KeyRequestType;
40 
41 using ::android::hardware::drm::V1_2::KeySetId;
42 using ::android::hardware::drm::V1_2::OfflineLicenseState;
43 using StatusV1_2 = ::android::hardware::drm::V1_2::Status;
44 
45 using ::android::hardware::hidl_string;
46 using ::android::hardware::hidl_memory;
47 
48 using ::android::hidl::allocator::V1_0::IAllocator;
49 
50 using std::random_device;
51 using std::mt19937;
52 
53 namespace android {
54 namespace hardware {
55 namespace drm {
56 namespace V1_2 {
57 namespace vts {
58 
59 const char *kCallbackLostState = "LostState";
60 const char *kCallbackKeysChange = "KeysChange";
61 
62 drm_vts::VendorModules *DrmHalTest::gVendorModules = nullptr;
63 
64 /**
65  * DrmHalPluginListener
66  */
67 
sendSessionLostState(const hidl_vec<uint8_t> & sessionId)68 Return<void> DrmHalPluginListener::sendSessionLostState(const hidl_vec<uint8_t>& sessionId) {
69     ListenerEventArgs args;
70     args.sessionId = sessionId;
71     NotifyFromCallback(kCallbackLostState, args);
72     return Void();
73 }
74 
sendKeysChange_1_2(const hidl_vec<uint8_t> & sessionId,const hidl_vec<KeyStatus> & keyStatusList,bool hasNewUsableKey)75 Return<void> DrmHalPluginListener::sendKeysChange_1_2(const hidl_vec<uint8_t>& sessionId,
76         const hidl_vec<KeyStatus>& keyStatusList, bool hasNewUsableKey) {
77     ListenerEventArgs args;
78     args.sessionId = sessionId;
79     args.keyStatusList = keyStatusList;
80     args.hasNewUsableKey = hasNewUsableKey;
81     NotifyFromCallback(kCallbackKeysChange, args);
82     return Void();
83 }
84 
getModuleForInstance(const std::string & instance)85 static DrmHalVTSVendorModule_V1* getModuleForInstance(const std::string& instance) {
86     if (instance == "clearkey" || instance == "default") {
87         return new DrmHalVTSClearkeyModule();
88     }
89 
90     return static_cast<DrmHalVTSVendorModule_V1*>(DrmHalTest::gVendorModules->getModuleByName(instance));
91 }
92 
93 /**
94  * DrmHalTest
95  */
96 
DrmHalTest()97 DrmHalTest::DrmHalTest() : vendorModule(getModuleForInstance(GetParamService())) {}
98 
SetUp()99 void DrmHalTest::SetUp() {
100     const ::testing::TestInfo* const test_info =
101             ::testing::UnitTest::GetInstance()->current_test_info();
102 
103     ALOGD("Running test %s.%s from (vendor) module %s",
104           test_info->test_case_name(), test_info->name(),
105           GetParamService().c_str());
106 
107     const string instance = GetParamService();
108 
109     drmFactory = IDrmFactory::getService(instance);
110     ASSERT_NE(drmFactory, nullptr);
111     drmPlugin = createDrmPlugin();
112 
113     cryptoFactory = ICryptoFactory::getService(instance);
114     ASSERT_NE(cryptoFactory, nullptr);
115     cryptoPlugin = createCryptoPlugin();
116 
117     if (!vendorModule) {
118         ASSERT_NE(instance, "widevine") << "Widevine requires vendor module.";
119         ASSERT_NE(instance, "clearkey") << "Clearkey requires vendor module.";
120         GTEST_SKIP() << "No vendor module installed";
121     }
122 
123     ASSERT_EQ(instance, vendorModule->getServiceName());
124     contentConfigurations = vendorModule->getContentConfigurations();
125 
126     // If drm scheme not installed skip subsequent tests
127     if (!drmFactory->isCryptoSchemeSupported(getUUID())) {
128         if (GetParamUUID() == hidl_array<uint8_t, 16>()) {
129             GTEST_SKIP() << "vendor module drm scheme not supported";
130         } else {
131             FAIL() << "param scheme must be supported: " << android::hardware::toString(GetParamUUID());
132         }
133     }
134 
135     ASSERT_NE(nullptr, drmPlugin.get()) << "Can't find " << vendorModule->getServiceName() <<  " [email protected] plugin";
136     ASSERT_NE(nullptr, cryptoPlugin.get()) << "Can't find " << vendorModule->getServiceName() <<  " [email protected] plugin";
137 
138 }
139 
createDrmPlugin()140 sp<IDrmPlugin> DrmHalTest::createDrmPlugin() {
141     if (drmFactory == nullptr) {
142         return nullptr;
143     }
144     sp<IDrmPlugin> plugin = nullptr;
145     hidl_string packageName("android.hardware.drm.test");
146     auto res =
147             drmFactory->createPlugin(getUUID(), packageName,
148                                      [&](StatusV1_0 status, const sp<IDrmPluginV1_0>& pluginV1_0) {
149                                          EXPECT_EQ(StatusV1_0::OK == status, pluginV1_0 != nullptr);
150                                          plugin = IDrmPlugin::castFrom(pluginV1_0);
151                                      });
152 
153     if (!res.isOk()) {
154         ALOGE("createDrmPlugin remote call failed");
155     }
156     return plugin;
157 }
158 
createCryptoPlugin()159 sp<ICryptoPlugin> DrmHalTest::createCryptoPlugin() {
160     if (cryptoFactory == nullptr) {
161         return nullptr;
162     }
163     sp<ICryptoPlugin> plugin = nullptr;
164     hidl_vec<uint8_t> initVec;
165     auto res = cryptoFactory->createPlugin(
166             getUUID(), initVec,
167             [&](StatusV1_0 status, const sp<ICryptoPluginV1_0>& pluginV1_0) {
168                 EXPECT_EQ(StatusV1_0::OK == status, pluginV1_0 != nullptr);
169                 plugin = ICryptoPlugin::castFrom(pluginV1_0);
170             });
171     if (!res.isOk()) {
172         ALOGE("createCryptoPlugin remote call failed");
173     }
174     return plugin;
175 }
176 
getUUID()177 hidl_array<uint8_t, 16> DrmHalTest::getUUID() {
178     if (GetParamUUID() == hidl_array<uint8_t, 16>()) {
179         return getVendorUUID();
180     }
181     return GetParamUUID();
182 }
183 
getVendorUUID()184 hidl_array<uint8_t, 16> DrmHalTest::getVendorUUID() {
185     if (vendorModule == nullptr) return {};
186     vector<uint8_t> uuid = vendorModule->getUUID();
187     return hidl_array<uint8_t, 16>(&uuid[0]);
188 }
189 
provision()190 void DrmHalTest::provision() {
191     hidl_string certificateType;
192     hidl_string certificateAuthority;
193     hidl_vec<uint8_t> provisionRequest;
194     hidl_string defaultUrl;
195     auto res = drmPlugin->getProvisionRequest_1_2(
196             certificateType, certificateAuthority,
197             [&](StatusV1_2 status, const hidl_vec<uint8_t>& request,
198                 const hidl_string& url) {
199                 if (status == StatusV1_2::OK) {
200                     EXPECT_NE(request.size(), 0u);
201                     provisionRequest = request;
202                     defaultUrl = url;
203                 } else if (status == StatusV1_2::ERROR_DRM_CANNOT_HANDLE) {
204                     EXPECT_EQ(0u, request.size());
205                 }
206             });
207     EXPECT_OK(res);
208 
209     if (provisionRequest.size() > 0) {
210         vector<uint8_t> response = vendorModule->handleProvisioningRequest(
211                 provisionRequest, defaultUrl);
212         ASSERT_NE(0u, response.size());
213 
214         auto res = drmPlugin->provideProvisionResponse(
215                 response, [&](StatusV1_0 status, const hidl_vec<uint8_t>&,
216                               const hidl_vec<uint8_t>&) {
217                     EXPECT_EQ(StatusV1_0::OK, status);
218                 });
219         EXPECT_OK(res);
220     }
221 }
222 
openSession(SecurityLevel level,StatusV1_0 * err)223 SessionId DrmHalTest::openSession(SecurityLevel level, StatusV1_0 *err) {
224     SessionId sessionId;
225     auto res = drmPlugin->openSession_1_1(level,
226         [&](StatusV1_0 status, const hidl_vec<unsigned char> &id) {
227             *err = status;
228             sessionId = id;
229     });
230     EXPECT_OK(res);
231     return sessionId;
232 }
233 
234 /**
235  * Helper method to open a session and verify that a non-empty
236  * session ID is returned
237  */
openSession()238 SessionId DrmHalTest::openSession() {
239     SessionId sessionId;
240 
241     auto res = drmPlugin->openSession([&](StatusV1_0 status, const hidl_vec<unsigned char> &id) {
242         EXPECT_EQ(StatusV1_0::OK, status);
243         EXPECT_NE(id.size(), 0u);
244         sessionId = id;
245     });
246     EXPECT_OK(res);
247     return sessionId;
248 }
249 
250 /**
251  * Helper method to close a session
252  */
closeSession(const SessionId & sessionId)253 void DrmHalTest::closeSession(const SessionId& sessionId) {
254     StatusV1_0 status = drmPlugin->closeSession(sessionId);
255     EXPECT_EQ(StatusV1_0::OK, status);
256 }
257 
getKeyRequest(const SessionId & sessionId,const DrmHalVTSVendorModule_V1::ContentConfiguration & configuration,const KeyType & type=KeyType::STREAMING)258 hidl_vec<uint8_t> DrmHalTest::getKeyRequest(
259     const SessionId& sessionId,
260     const DrmHalVTSVendorModule_V1::ContentConfiguration& configuration,
261     const KeyType& type = KeyType::STREAMING) {
262     hidl_vec<uint8_t> keyRequest;
263     auto res = drmPlugin->getKeyRequest_1_2(
264         sessionId, configuration.initData, configuration.mimeType, type,
265         toHidlKeyedVector(configuration.optionalParameters),
266         [&](Status status, const hidl_vec<uint8_t>& request,
267             KeyRequestType requestType, const hidl_string&) {
268             EXPECT_EQ(Status::OK, status) << "Failed to get "
269                                              "key request for configuration "
270                                           << configuration.name;
271             if (type == KeyType::RELEASE) {
272                 EXPECT_EQ(KeyRequestType::RELEASE, requestType);
273             } else {
274                 EXPECT_EQ(KeyRequestType::INITIAL, requestType);
275             }
276             EXPECT_NE(request.size(), 0u) << "Expected key request size"
277                                              " to have length > 0 bytes";
278             keyRequest = request;
279         });
280     EXPECT_OK(res);
281     return keyRequest;
282 }
283 
getContent(const KeyType & type) const284 DrmHalVTSVendorModule_V1::ContentConfiguration DrmHalTest::getContent(const KeyType& type) const {
285     for (const auto& config : contentConfigurations) {
286         if (type != KeyType::OFFLINE || config.policy.allowOffline) {
287             return config;
288         }
289     }
290     EXPECT_TRUE(false) << "no content configurations found";
291     return {};
292 }
293 
provideKeyResponse(const SessionId & sessionId,const hidl_vec<uint8_t> & keyResponse)294 hidl_vec<uint8_t> DrmHalTest::provideKeyResponse(
295     const SessionId& sessionId,
296     const hidl_vec<uint8_t>& keyResponse) {
297     hidl_vec<uint8_t> keySetId;
298     auto res = drmPlugin->provideKeyResponse(
299         sessionId, keyResponse,
300         [&](StatusV1_0 status, const hidl_vec<uint8_t>& myKeySetId) {
301             EXPECT_EQ(StatusV1_0::OK, status) << "Failure providing "
302                                                  "key response for configuration ";
303             keySetId = myKeySetId;
304         });
305     EXPECT_OK(res);
306     return keySetId;
307 }
308 
309 /**
310  * Helper method to load keys for subsequent decrypt tests.
311  * These tests use predetermined key request/response to
312  * avoid requiring a round trip to a license server.
313  */
loadKeys(const SessionId & sessionId,const DrmHalVTSVendorModule_V1::ContentConfiguration & configuration,const KeyType & type)314 hidl_vec<uint8_t> DrmHalTest::loadKeys(
315     const SessionId& sessionId,
316     const DrmHalVTSVendorModule_V1::ContentConfiguration& configuration,
317     const KeyType& type) {
318     hidl_vec<uint8_t> keyRequest = getKeyRequest(sessionId, configuration, type);
319 
320     /**
321      * Get key response from vendor module
322      */
323     hidl_vec<uint8_t> keyResponse =
324         vendorModule->handleKeyRequest(keyRequest, configuration.serverUrl);
325     EXPECT_NE(keyResponse.size(), 0u) << "Expected key response size "
326                                          "to have length > 0 bytes";
327 
328     return provideKeyResponse(sessionId, keyResponse);
329 }
330 
loadKeys(const SessionId & sessionId,const KeyType & type)331 hidl_vec<uint8_t> DrmHalTest::loadKeys(
332         const SessionId& sessionId,
333         const KeyType& type) {
334     return loadKeys(sessionId, getContent(type), type);
335 }
336 
toHidlKeyedVector(const map<string,string> & params)337 KeyedVector DrmHalTest::toHidlKeyedVector(
338     const map<string, string>& params) {
339     std::vector<KeyValue> stdKeyedVector;
340     for (auto it = params.begin(); it != params.end(); ++it) {
341         KeyValue keyValue;
342         keyValue.key = it->first;
343         keyValue.value = it->second;
344         stdKeyedVector.push_back(keyValue);
345     }
346     return KeyedVector(stdKeyedVector);
347 }
348 
toHidlArray(const vector<uint8_t> & vec)349 hidl_array<uint8_t, 16> DrmHalTest::toHidlArray(const vector<uint8_t>& vec) {
350     EXPECT_EQ(16u, vec.size());
351     return hidl_array<uint8_t, 16>(&vec[0]);
352 }
353 
354 /**
355  * getDecryptMemory allocates memory for decryption, then sets it
356  * as a shared buffer base in the crypto hal.  The allocated and
357  * mapped IMemory is returned.
358  *
359  * @param size the size of the memory segment to allocate
360  * @param the index of the memory segment which will be used
361  * to refer to it for decryption.
362  */
getDecryptMemory(size_t size,size_t index)363 sp<IMemory> DrmHalTest::getDecryptMemory(size_t size, size_t index) {
364     sp<IAllocator> ashmemAllocator = IAllocator::getService("ashmem");
365     EXPECT_NE(nullptr, ashmemAllocator.get());
366 
367     hidl_memory hidlMemory;
368     auto res = ashmemAllocator->allocate(
369             size, [&](bool success, const hidl_memory& memory) {
370                 EXPECT_EQ(success, true);
371                 EXPECT_EQ(memory.size(), size);
372                 hidlMemory = memory;
373             });
374 
375     EXPECT_OK(res);
376 
377     sp<IMemory> mappedMemory = mapMemory(hidlMemory);
378     EXPECT_NE(nullptr, mappedMemory.get());
379     res = cryptoPlugin->setSharedBufferBase(hidlMemory, index);
380     EXPECT_OK(res);
381     return mappedMemory;
382 }
383 
fillRandom(const sp<IMemory> & memory)384 void DrmHalTest::fillRandom(const sp<IMemory>& memory) {
385     random_device rd;
386     mt19937 rand(rd());
387     for (size_t i = 0; i < memory->getSize() / sizeof(uint32_t); i++) {
388         auto p = static_cast<uint32_t*>(
389                 static_cast<void*>(memory->getPointer()));
390         p[i] = rand();
391     }
392 }
393 
decrypt(Mode mode,bool isSecure,const hidl_array<uint8_t,16> & keyId,uint8_t * iv,const hidl_vec<SubSample> & subSamples,const Pattern & pattern,const vector<uint8_t> & key,StatusV1_2 expectedStatus)394 uint32_t DrmHalTest::decrypt(Mode mode, bool isSecure,
395         const hidl_array<uint8_t, 16>& keyId, uint8_t* iv,
396         const hidl_vec<SubSample>& subSamples, const Pattern& pattern,
397         const vector<uint8_t>& key, StatusV1_2 expectedStatus) {
398     const size_t kSegmentIndex = 0;
399 
400     uint8_t localIv[AES_BLOCK_SIZE];
401     memcpy(localIv, iv, AES_BLOCK_SIZE);
402 
403     size_t totalSize = 0;
404     for (size_t i = 0; i < subSamples.size(); i++) {
405         totalSize += subSamples[i].numBytesOfClearData;
406         totalSize += subSamples[i].numBytesOfEncryptedData;
407     }
408 
409     // The first totalSize bytes of shared memory is the encrypted
410     // input, the second totalSize bytes (if exists) is the decrypted output.
411     size_t factor = expectedStatus == StatusV1_2::ERROR_DRM_FRAME_TOO_LARGE ? 1 : 2;
412     sp<IMemory> sharedMemory =
413             getDecryptMemory(totalSize * factor, kSegmentIndex);
414 
415     const SharedBuffer sourceBuffer = {
416         .bufferId = kSegmentIndex, .offset = 0, .size = totalSize};
417     fillRandom(sharedMemory);
418 
419     const DestinationBuffer destBuffer = {.type = BufferType::SHARED_MEMORY,
420                                           {.bufferId = kSegmentIndex,
421                                            .offset = totalSize,
422                                            .size = totalSize},
423                                           .secureMemory = nullptr};
424     const uint64_t offset = 0;
425     uint32_t bytesWritten = 0;
426     auto res = cryptoPlugin->decrypt_1_2(isSecure, keyId, localIv, mode, pattern,
427             subSamples, sourceBuffer, offset, destBuffer,
428             [&](StatusV1_2 status, uint32_t count, string detailedError) {
429                 EXPECT_EQ(expectedStatus, status) << "Unexpected decrypt status " <<
430                 detailedError;
431                 bytesWritten = count;
432             });
433     EXPECT_OK(res);
434 
435     if (bytesWritten != totalSize) {
436         return bytesWritten;
437     }
438     uint8_t* base = static_cast<uint8_t*>(
439             static_cast<void*>(sharedMemory->getPointer()));
440 
441     // generate reference vector
442     vector<uint8_t> reference(totalSize);
443 
444     memcpy(localIv, iv, AES_BLOCK_SIZE);
445     switch (mode) {
446     case Mode::UNENCRYPTED:
447         memcpy(&reference[0], base, totalSize);
448         break;
449     case Mode::AES_CTR:
450         aes_ctr_decrypt(&reference[0], base, localIv, subSamples, key);
451         break;
452     case Mode::AES_CBC:
453         aes_cbc_decrypt(&reference[0], base, localIv, subSamples, key);
454         break;
455     case Mode::AES_CBC_CTS:
456         EXPECT_TRUE(false) << "AES_CBC_CTS mode not supported";
457         break;
458     }
459 
460     // compare reference to decrypted data which is at base + total size
461     EXPECT_EQ(0, memcmp(static_cast<void *>(&reference[0]),
462                         static_cast<void*>(base + totalSize), totalSize))
463             << "decrypt data mismatch";
464     return totalSize;
465 }
466 
467 /**
468  * Decrypt a list of clear+encrypted subsamples using the specified key
469  * in AES-CTR mode
470  */
aes_ctr_decrypt(uint8_t * dest,uint8_t * src,uint8_t * iv,const hidl_vec<SubSample> & subSamples,const vector<uint8_t> & key)471 void DrmHalTest::aes_ctr_decrypt(uint8_t* dest, uint8_t* src,
472         uint8_t* iv, const hidl_vec<SubSample>& subSamples,
473         const vector<uint8_t>& key) {
474     AES_KEY decryptionKey;
475     AES_set_encrypt_key(&key[0], 128, &decryptionKey);
476 
477     size_t offset = 0;
478     unsigned int blockOffset = 0;
479     uint8_t previousEncryptedCounter[AES_BLOCK_SIZE];
480     memset(previousEncryptedCounter, 0, AES_BLOCK_SIZE);
481 
482     for (size_t i = 0; i < subSamples.size(); i++) {
483         const SubSample& subSample = subSamples[i];
484 
485         if (subSample.numBytesOfClearData > 0) {
486             memcpy(dest + offset, src + offset, subSample.numBytesOfClearData);
487             offset += subSample.numBytesOfClearData;
488         }
489 
490         if (subSample.numBytesOfEncryptedData > 0) {
491             AES_ctr128_encrypt(src + offset, dest + offset,
492                     subSample.numBytesOfEncryptedData, &decryptionKey,
493                     iv, previousEncryptedCounter, &blockOffset);
494             offset += subSample.numBytesOfEncryptedData;
495         }
496     }
497 }
498 
499 /**
500  * Decrypt a list of clear+encrypted subsamples using the specified key
501  * in AES-CBC mode
502  */
aes_cbc_decrypt(uint8_t * dest,uint8_t * src,uint8_t * iv,const hidl_vec<SubSample> & subSamples,const vector<uint8_t> & key)503 void DrmHalTest::aes_cbc_decrypt(uint8_t* dest, uint8_t* src,
504         uint8_t* iv, const hidl_vec<SubSample>& subSamples,
505         const vector<uint8_t>& key) {
506     AES_KEY decryptionKey;
507     AES_set_encrypt_key(&key[0], 128, &decryptionKey);
508 
509     size_t offset = 0;
510     for (size_t i = 0; i < subSamples.size(); i++) {
511         memcpy(dest + offset, src + offset, subSamples[i].numBytesOfClearData);
512         offset += subSamples[i].numBytesOfClearData;
513 
514         AES_cbc_encrypt(src + offset, dest + offset, subSamples[i].numBytesOfEncryptedData,
515                 &decryptionKey, iv, 0 /* decrypt */);
516         offset += subSamples[i].numBytesOfEncryptedData;
517     }
518 }
519 
520 /**
521  * Helper method to test decryption with invalid keys is returned
522  */
decryptWithInvalidKeys(hidl_vec<uint8_t> & invalidResponse,vector<uint8_t> & iv,const Pattern & noPattern,const vector<SubSample> & subSamples)523 void DrmHalClearkeyTestV1_2::decryptWithInvalidKeys(
524         hidl_vec<uint8_t>& invalidResponse,
525         vector<uint8_t>& iv,
526         const Pattern& noPattern,
527         const vector<SubSample>& subSamples) {
528     DrmHalVTSVendorModule_V1::ContentConfiguration content = getContent();
529     if (content.keys.empty()) {
530         FAIL() << "no keys";
531     }
532 
533     const auto& key = content.keys[0];
534     auto sessionId = openSession();
535     auto res = drmPlugin->provideKeyResponse(
536         sessionId, invalidResponse,
537         [&](StatusV1_0 status, const hidl_vec<uint8_t>& myKeySetId) {
538             EXPECT_EQ(StatusV1_0::OK, status);
539             EXPECT_EQ(0u, myKeySetId.size());
540         });
541     EXPECT_OK(res);
542 
543     EXPECT_TRUE(cryptoPlugin->setMediaDrmSession(sessionId).isOk());
544 
545     uint32_t byteCount = decrypt(Mode::AES_CTR, key.isSecure,
546             toHidlArray(key.keyId), &iv[0], subSamples, noPattern,
547             key.clearContentKey, Status::ERROR_DRM_NO_LICENSE);
548     EXPECT_EQ(0u, byteCount);
549 
550     closeSession(sessionId);
551 }
552 
553 }  // namespace vts
554 }  // namespace V1_2
555 }  // namespace drm
556 }  // namespace hardware
557 }  // namespace android
558