| /* |
| * Copyright (C) 2021 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| #define LOG_TAG "clearkey-DrmPlugin" |
| |
| #include <aidl/android/hardware/drm/DrmMetric.h> |
| #include <utils/Log.h> |
| |
| #include <inttypes.h> |
| #include <stdio.h> |
| #include <chrono> |
| #include <set> |
| |
| #include "AidlUtils.h" |
| #include "ClearKeyDrmProperties.h" |
| #include "DrmPlugin.h" |
| #include "Session.h" |
| #include "Utils.h" |
| #include "AidlClearKeryProperties.h" |
| |
| namespace { |
| const std::string kKeySetIdPrefix("ckid"); |
| const int kKeySetIdLength = 16; |
| const int kSecureStopIdStart = 100; |
| const std::string kOfflineLicense("\"type\":\"persistent-license\""); |
| const std::string kStreaming("Streaming"); |
| const std::string kTemporaryLicense("\"type\":\"temporary\""); |
| const std::string kTrue("True"); |
| |
| const std::string kQueryKeyLicenseType("LicenseType"); |
| // Value: "Streaming" or "Offline" |
| const std::string kQueryKeyPlayAllowed("PlayAllowed"); |
| // Value: "True" or "False" |
| const std::string kQueryKeyRenewAllowed("RenewAllowed"); |
| // Value: "True" or "False" |
| |
| const int kSecureStopIdSize = 10; |
| |
| std::vector<uint8_t> uint32ToVector(uint32_t value) { |
| // 10 bytes to display max value 4294967295 + one byte null terminator |
| char buffer[kSecureStopIdSize]; |
| memset(buffer, 0, kSecureStopIdSize); |
| snprintf(buffer, kSecureStopIdSize, "%" PRIu32, value); |
| return std::vector<uint8_t>(buffer, buffer + sizeof(buffer)); |
| } |
| |
| }; // unnamed namespace |
| |
| namespace aidl { |
| namespace android { |
| namespace hardware { |
| namespace drm { |
| namespace clearkey { |
| |
| using ::android::Mutex; |
| |
| DrmPlugin::DrmPlugin(SessionLibrary* sessionLibrary) |
| : mSessionLibrary(sessionLibrary), |
| mOpenSessionOkCount(0), |
| mCloseSessionOkCount(0), |
| mCloseSessionNotOpenedCount(0), |
| mNextSecureStopId(kSecureStopIdStart), |
| mMockError(Status::OK) { |
| mPlayPolicy.clear(); |
| initProperties(); |
| mSecureStops.clear(); |
| mReleaseKeysMap.clear(); |
| std::srand(std::time(nullptr)); |
| } |
| |
| void DrmPlugin::initProperties() { |
| mStringProperties.clear(); |
| mStringProperties[kVendorKey] = kAidlVendorValue; |
| mStringProperties[kVersionKey] = kAidlVersionValue; |
| mStringProperties[kPluginDescriptionKey] = kAidlPluginDescriptionValue; |
| mStringProperties[kAlgorithmsKey] = kAidlAlgorithmsValue; |
| mStringProperties[kListenerTestSupportKey] = kAidlListenerTestSupportValue; |
| mStringProperties[kDrmErrorTestKey] = kAidlDrmErrorTestValue; |
| mStringProperties[kAidlVersionKey] = kAidlVersionValue; |
| |
| std::vector<uint8_t> valueVector; |
| valueVector.clear(); |
| valueVector.insert(valueVector.end(), kTestDeviceIdData, |
| kTestDeviceIdData + sizeof(kTestDeviceIdData) / sizeof(uint8_t)); |
| mByteArrayProperties[kDeviceIdKey] = valueVector; |
| |
| valueVector.clear(); |
| valueVector.insert(valueVector.end(), kMetricsData, |
| kMetricsData + sizeof(kMetricsData) / sizeof(uint8_t)); |
| mByteArrayProperties[kMetricsKey] = valueVector; |
| } |
| |
| // The secure stop in ClearKey implementation is not installed securely. |
| // This function merely creates a test environment for testing secure stops APIs. |
| // The content in this secure stop is implementation dependent, the clearkey |
| // secureStop does not serve as a reference implementation. |
| void DrmPlugin::installSecureStop(const std::vector<uint8_t>& sessionId) { |
| ::android::Mutex::Autolock lock(mSecureStopLock); |
| |
| ClearkeySecureStop clearkeySecureStop; |
| clearkeySecureStop.id = uint32ToVector(++mNextSecureStopId); |
| clearkeySecureStop.data.assign(sessionId.begin(), sessionId.end()); |
| |
| mSecureStops.insert(std::pair<std::vector<uint8_t>, ClearkeySecureStop>(clearkeySecureStop.id, |
| clearkeySecureStop)); |
| } |
| |
| ::ndk::ScopedAStatus DrmPlugin::closeSession(const std::vector<uint8_t>& in_sessionId) { |
| if (in_sessionId.size() == 0) { |
| return toNdkScopedAStatus(Status::BAD_VALUE); |
| } |
| |
| ::android::sp<Session> session = mSessionLibrary->findSession(in_sessionId); |
| if (session.get()) { |
| mSessionLibrary->destroySession(session); |
| if (session->getMockError() != clearkeydrm::OK) { |
| sendSessionLostState(in_sessionId); |
| return toNdkScopedAStatus(Status::ERROR_DRM_INVALID_STATE); |
| } |
| mCloseSessionOkCount++; |
| return toNdkScopedAStatus(Status::OK); |
| } |
| mCloseSessionNotOpenedCount++; |
| return toNdkScopedAStatus(Status::ERROR_DRM_SESSION_NOT_OPENED); |
| } |
| |
| ::ndk::ScopedAStatus DrmPlugin::decrypt(const std::vector<uint8_t>& in_sessionId, |
| const std::vector<uint8_t>& in_keyId, |
| const std::vector<uint8_t>& in_input, |
| const std::vector<uint8_t>& in_iv, |
| std::vector<uint8_t>* _aidl_return) { |
| *_aidl_return = {}; |
| if (in_sessionId.size() == 0 || in_keyId.size() == 0 || in_input.size() == 0 || |
| in_iv.size() == 0) { |
| return toNdkScopedAStatus(Status::BAD_VALUE); |
| } |
| return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE); |
| } |
| |
| ::ndk::ScopedAStatus DrmPlugin::encrypt(const std::vector<uint8_t>& in_sessionId, |
| const std::vector<uint8_t>& in_keyId, |
| const std::vector<uint8_t>& in_input, |
| const std::vector<uint8_t>& in_iv, |
| std::vector<uint8_t>* _aidl_return) { |
| *_aidl_return = {}; |
| if (in_sessionId.size() == 0 || in_keyId.size() == 0 || in_input.size() == 0 || |
| in_iv.size() == 0) { |
| return toNdkScopedAStatus(Status::BAD_VALUE); |
| } |
| return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE); |
| } |
| |
| ::ndk::ScopedAStatus DrmPlugin::getHdcpLevels( |
| ::aidl::android::hardware::drm::HdcpLevels* _aidl_return) { |
| _aidl_return->connectedLevel = HdcpLevel::HDCP_NONE; |
| _aidl_return->maxLevel = HdcpLevel::HDCP_NO_OUTPUT; |
| return toNdkScopedAStatus(Status::OK); |
| } |
| |
| ::ndk::ScopedAStatus DrmPlugin::getKeyRequest( |
| const std::vector<uint8_t>& in_scope, const std::vector<uint8_t>& in_initData, |
| const std::string& in_mimeType, ::aidl::android::hardware::drm::KeyType in_keyType, |
| const std::vector<::aidl::android::hardware::drm::KeyValue>& in_optionalParameters, |
| ::aidl::android::hardware::drm::KeyRequest* _aidl_return) { |
| UNUSED(in_optionalParameters); |
| |
| KeyRequestType keyRequestType = KeyRequestType::UNKNOWN; |
| std::string defaultUrl(""); |
| |
| _aidl_return->request = {}; |
| _aidl_return->requestType = keyRequestType; |
| _aidl_return->defaultUrl = defaultUrl; |
| |
| if (in_scope.size() == 0 || |
| (in_keyType != KeyType::STREAMING && in_keyType != KeyType::OFFLINE && |
| in_keyType != KeyType::RELEASE)) { |
| return toNdkScopedAStatus(Status::BAD_VALUE); |
| } |
| |
| const std::vector<uint8_t> scopeId = in_scope; |
| ::android::sp<Session> session; |
| std::set<KeyType> init_types{KeyType::STREAMING, KeyType::OFFLINE}; |
| if (init_types.count(in_keyType)) { |
| std::vector<uint8_t> sessionId(scopeId.begin(), scopeId.end()); |
| session = mSessionLibrary->findSession(sessionId); |
| if (!session.get()) { |
| return toNdkScopedAStatus(Status::ERROR_DRM_SESSION_NOT_OPENED); |
| } else if (session->getMockError() != clearkeydrm::OK) { |
| return toNdkScopedAStatus(session->getMockError()); |
| } |
| keyRequestType = KeyRequestType::INITIAL; |
| } |
| |
| std::vector<uint8_t> request = {}; |
| auto keyType = static_cast<CdmKeyType>(in_keyType); |
| auto status = session->getKeyRequest(in_initData, in_mimeType, keyType, &request); |
| |
| if (in_keyType == KeyType::RELEASE) { |
| std::vector<uint8_t> keySetId(scopeId.begin(), scopeId.end()); |
| std::string requestString(request.begin(), request.end()); |
| if (requestString.find(kOfflineLicense) != std::string::npos) { |
| std::string emptyResponse; |
| std::string keySetIdString(keySetId.begin(), keySetId.end()); |
| if (!mFileHandle.StoreLicense(keySetIdString, DeviceFiles::kLicenseStateReleasing, |
| emptyResponse)) { |
| ALOGE("Problem releasing offline license"); |
| return toNdkScopedAStatus(Status::ERROR_DRM_UNKNOWN); |
| } |
| if (mReleaseKeysMap.find(keySetIdString) == mReleaseKeysMap.end()) { |
| ::android::sp<Session> session = mSessionLibrary->createSession(); |
| mReleaseKeysMap[keySetIdString] = session->sessionId(); |
| } else { |
| ALOGI("key is in use, ignore release request"); |
| } |
| } else { |
| ALOGE("Offline license not found, nothing to release"); |
| } |
| keyRequestType = KeyRequestType::RELEASE; |
| } |
| _aidl_return->request = request; |
| _aidl_return->requestType = keyRequestType; |
| _aidl_return->defaultUrl = defaultUrl; |
| return toNdkScopedAStatus(status); |
| } |
| |
| ::ndk::ScopedAStatus DrmPlugin::getLogMessages( |
| std::vector<::aidl::android::hardware::drm::LogMessage>* _aidl_return) { |
| using std::chrono::duration_cast; |
| using std::chrono::milliseconds; |
| using std::chrono::system_clock; |
| |
| auto timeMillis = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count(); |
| |
| std::vector<::aidl::android::hardware::drm::LogMessage> logs = { |
| {timeMillis, ::aidl::android::hardware::drm::LogPriority::ERROR, |
| std::string("Not implemented")}}; |
| *_aidl_return = logs; |
| return toNdkScopedAStatus(Status::OK); |
| } |
| |
| ::ndk::ScopedAStatus DrmPlugin::getMetrics( |
| std::vector<::aidl::android::hardware::drm::DrmMetricGroup>* _aidl_return) { |
| // Set the open session count metric. |
| DrmMetricNamedValue openSessionOkAttribute = {"status", static_cast<int64_t>(Status::OK)}; |
| DrmMetricNamedValue openSessionMetricValue = {"count", mOpenSessionOkCount}; |
| DrmMetric openSessionMetric = { |
| "open_session", {openSessionOkAttribute}, {openSessionMetricValue}}; |
| |
| // Set the close session count metric. |
| DrmMetricNamedValue closeSessionOkAttribute = {"status", static_cast<int64_t>(Status::OK)}; |
| DrmMetricNamedValue closeSessionMetricValue = {"count", mCloseSessionOkCount}; |
| DrmMetric closeSessionMetric = { |
| "close_session", {closeSessionOkAttribute}, {closeSessionMetricValue}}; |
| |
| // Set the close session, not opened metric. |
| DrmMetricNamedValue closeSessionNotOpenedAttribute = {"status", |
| static_cast<int64_t>(Status::ERROR_DRM_SESSION_NOT_OPENED)}; |
| DrmMetricNamedValue closeSessionNotOpenedMetricValue = {"count", mCloseSessionNotOpenedCount}; |
| DrmMetric closeSessionNotOpenedMetric = { |
| "close_session", {closeSessionNotOpenedAttribute}, {closeSessionNotOpenedMetricValue}}; |
| |
| // Set the setPlaybackId metric. |
| std::vector<DrmMetricNamedValue> sids = {}; |
| std::vector<DrmMetricNamedValue> playbackIds = {}; |
| for (const auto& [key, value] : mPlaybackId) { |
| std::string sid(key.begin(), key.end()); |
| DrmMetricNamedValue sessionIdAttribute = {"sid", sid}; |
| sids.push_back(sessionIdAttribute); |
| |
| DrmMetricNamedValue playbackIdMetricValue = {"playbackId", value}; |
| playbackIds.push_back(playbackIdMetricValue); |
| } |
| DrmMetric setPlaybackIdMetric = {"set_playback_id", sids, playbackIds}; |
| |
| DrmMetricGroup metrics = {{openSessionMetric, closeSessionMetric, closeSessionNotOpenedMetric, |
| setPlaybackIdMetric}}; |
| |
| *_aidl_return = {metrics}; |
| return toNdkScopedAStatus(Status::OK); |
| } |
| |
| ::ndk::ScopedAStatus DrmPlugin::getNumberOfSessions( |
| ::aidl::android::hardware::drm::NumberOfSessions* _aidl_return) { |
| _aidl_return->currentSessions = mSessionLibrary->numOpenSessions(); |
| _aidl_return->maxSessions = 10; |
| return toNdkScopedAStatus(Status::OK); |
| } |
| |
| ::ndk::ScopedAStatus DrmPlugin::getOfflineLicenseKeySetIds( |
| std::vector<::aidl::android::hardware::drm::KeySetId>* _aidl_return) { |
| std::vector<std::string> licenseNames = mFileHandle.ListLicenses(); |
| std::vector<KeySetId> keySetIds = {}; |
| if (mMockError != Status::OK) { |
| *_aidl_return = keySetIds; |
| return toNdkScopedAStatus(toMockStatus(mMockError)); |
| } |
| for (const auto& name : licenseNames) { |
| std::vector<uint8_t> keySetId(name.begin(), name.end()); |
| KeySetId id = {}; |
| id.keySetId = keySetId; |
| keySetIds.push_back(id); |
| } |
| *_aidl_return = keySetIds; |
| return toNdkScopedAStatus(Status::OK); |
| } |
| |
| ::ndk::ScopedAStatus DrmPlugin::getOfflineLicenseState( |
| const ::aidl::android::hardware::drm::KeySetId& in_keySetId, |
| ::aidl::android::hardware::drm::OfflineLicenseState* _aidl_return) { |
| std::string licenseName(in_keySetId.keySetId.begin(), in_keySetId.keySetId.end()); |
| DeviceFiles::LicenseState state; |
| std::string license; |
| OfflineLicenseState licenseState = OfflineLicenseState::UNKNOWN; |
| Status status = Status::OK; |
| if (mMockError != Status::OK) { |
| *_aidl_return = licenseState; |
| return toNdkScopedAStatus(toMockStatus(mMockError)); |
| } else if (mFileHandle.RetrieveLicense(licenseName, &state, &license)) { |
| switch (state) { |
| case DeviceFiles::kLicenseStateActive: |
| licenseState = OfflineLicenseState::USABLE; |
| break; |
| case DeviceFiles::kLicenseStateReleasing: |
| licenseState = OfflineLicenseState::INACTIVE; |
| break; |
| case DeviceFiles::kLicenseStateUnknown: |
| licenseState = OfflineLicenseState::UNKNOWN; |
| break; |
| } |
| } else { |
| status = Status::BAD_VALUE; |
| } |
| *_aidl_return = licenseState; |
| return toNdkScopedAStatus(status); |
| } |
| |
| ::ndk::ScopedAStatus DrmPlugin::getPropertyByteArray(const std::string& in_propertyName, |
| std::vector<uint8_t>* _aidl_return) { |
| std::map<std::string, std::vector<uint8_t>>::iterator itr = |
| mByteArrayProperties.find(std::string(in_propertyName.c_str())); |
| Status status = Status::OK; |
| if (itr != mByteArrayProperties.end()) { |
| *_aidl_return = itr->second; |
| } else { |
| ALOGE("App requested unknown property: %s", in_propertyName.c_str()); |
| status = Status::BAD_VALUE; |
| *_aidl_return = {}; |
| } |
| return toNdkScopedAStatus(status); |
| } |
| |
| ::ndk::ScopedAStatus DrmPlugin::getPropertyString(const std::string& in_propertyName, |
| std::string* _aidl_return) { |
| std::string name(in_propertyName.c_str()); |
| std::string value; |
| Status status = Status::OK; |
| |
| if (name == kVendorKey) { |
| value = mStringProperties[kVendorKey]; |
| } else if (name == kVersionKey) { |
| value = mStringProperties[kVersionKey]; |
| } else if (name == kPluginDescriptionKey) { |
| value = mStringProperties[kPluginDescriptionKey]; |
| } else if (name == kAlgorithmsKey) { |
| value = mStringProperties[kAlgorithmsKey]; |
| } else if (name == kListenerTestSupportKey) { |
| value = mStringProperties[kListenerTestSupportKey]; |
| } else if (name == kDrmErrorTestKey) { |
| value = mStringProperties[kDrmErrorTestKey]; |
| } else if (name == kAidlVersionKey) { |
| value = mStringProperties[kAidlVersionKey]; |
| } else { |
| ALOGE("App requested unknown string property %s", name.c_str()); |
| status = Status::ERROR_DRM_CANNOT_HANDLE; |
| } |
| *_aidl_return = value; |
| return toNdkScopedAStatus(status); |
| } |
| |
| ::ndk::ScopedAStatus DrmPlugin::getProvisionRequest( |
| const std::string& in_certificateType, const std::string& in_certificateAuthority, |
| ::aidl::android::hardware::drm::ProvisionRequest* _aidl_return) { |
| UNUSED(in_certificateType); |
| UNUSED(in_certificateAuthority); |
| _aidl_return->request = {}; |
| _aidl_return->defaultUrl = {}; |
| return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE); |
| } |
| |
| ::ndk::ScopedAStatus DrmPlugin::getSecureStop( |
| const ::aidl::android::hardware::drm::SecureStopId& in_secureStopId, |
| ::aidl::android::hardware::drm::SecureStop* _aidl_return) { |
| std::vector<uint8_t> stop = {}; |
| |
| mSecureStopLock.lock(); |
| auto itr = mSecureStops.find(in_secureStopId.secureStopId); |
| if (itr != mSecureStops.end()) { |
| ClearkeySecureStop clearkeyStop = itr->second; |
| stop.insert(stop.end(), clearkeyStop.id.begin(), clearkeyStop.id.end()); |
| stop.insert(stop.end(), clearkeyStop.data.begin(), clearkeyStop.data.end()); |
| } |
| mSecureStopLock.unlock(); |
| |
| SecureStop secureStop = {}; |
| Status status = Status::OK; |
| if (!stop.empty()) { |
| secureStop.opaqueData = stop; |
| } else { |
| status = Status::BAD_VALUE; |
| } |
| *_aidl_return = secureStop; |
| return toNdkScopedAStatus(status); |
| } |
| |
| ::ndk::ScopedAStatus DrmPlugin::getSecureStopIds( |
| std::vector<::aidl::android::hardware::drm::SecureStopId>* _aidl_return) { |
| mSecureStopLock.lock(); |
| std::vector<::aidl::android::hardware::drm::SecureStopId> ids; |
| for (auto itr = mSecureStops.begin(); itr != mSecureStops.end(); ++itr) { |
| SecureStopId id; |
| id.secureStopId = itr->first; |
| ids.push_back(id); |
| } |
| mSecureStopLock.unlock(); |
| |
| *_aidl_return = ids; |
| return toNdkScopedAStatus(Status::OK); |
| } |
| |
| ::ndk::ScopedAStatus DrmPlugin::getSecureStops( |
| std::vector<::aidl::android::hardware::drm::SecureStop>* _aidl_return) { |
| mSecureStopLock.lock(); |
| std::vector<::aidl::android::hardware::drm::SecureStop> stops; |
| for (auto itr = mSecureStops.begin(); itr != mSecureStops.end(); ++itr) { |
| ClearkeySecureStop clearkeyStop = itr->second; |
| std::vector<uint8_t> stop{}; |
| stop.insert(stop.end(), clearkeyStop.id.begin(), clearkeyStop.id.end()); |
| stop.insert(stop.end(), clearkeyStop.data.begin(), clearkeyStop.data.end()); |
| |
| SecureStop secureStop; |
| secureStop.opaqueData = stop; |
| stops.push_back(secureStop); |
| } |
| mSecureStopLock.unlock(); |
| |
| *_aidl_return = stops; |
| return toNdkScopedAStatus(Status::OK); |
| } |
| |
| ::ndk::ScopedAStatus DrmPlugin::getSecurityLevel( |
| const std::vector<uint8_t>& in_sessionId, |
| ::aidl::android::hardware::drm::SecurityLevel* _aidl_return) { |
| if (in_sessionId.size() == 0) { |
| *_aidl_return = ::aidl::android::hardware::drm::SecurityLevel::UNKNOWN; |
| return toNdkScopedAStatus(Status::BAD_VALUE); |
| } |
| |
| std::vector<uint8_t> sid = in_sessionId; |
| ::android::sp<Session> session = mSessionLibrary->findSession(sid); |
| if (!session.get()) { |
| *_aidl_return = SecurityLevel::UNKNOWN; |
| return toNdkScopedAStatus(Status::ERROR_DRM_SESSION_NOT_OPENED); |
| } |
| |
| Mutex::Autolock lock(mSecurityLevelLock); |
| std::map<std::vector<uint8_t>, ::aidl::android::hardware::drm::SecurityLevel>::iterator itr = |
| mSecurityLevel.find(sid); |
| if (itr == mSecurityLevel.end()) { |
| ALOGE("Session id not found"); |
| *_aidl_return = SecurityLevel::UNKNOWN; |
| return toNdkScopedAStatus(Status::ERROR_DRM_INVALID_STATE); |
| } |
| |
| *_aidl_return = SecurityLevel::SW_SECURE_CRYPTO; |
| return toNdkScopedAStatus(Status::OK); |
| } |
| |
| ::ndk::ScopedAStatus DrmPlugin::openSession( |
| ::aidl::android::hardware::drm::SecurityLevel in_securityLevel, |
| std::vector<uint8_t>* _aidl_return) { |
| ::android::sp<Session> session = mSessionLibrary->createSession(); |
| processMockError(session); |
| std::vector<uint8_t> sessionId = session->sessionId(); |
| |
| Status status = setSecurityLevel(sessionId, in_securityLevel); |
| if (status == Status::OK) { |
| mOpenSessionOkCount++; |
| } else { |
| mSessionLibrary->destroySession(session); |
| sessionId.clear(); |
| } |
| *_aidl_return = sessionId; |
| return toNdkScopedAStatus(status); |
| } |
| |
| ::ndk::ScopedAStatus DrmPlugin::provideKeyResponse( |
| const std::vector<uint8_t>& in_scope, const std::vector<uint8_t>& in_response, |
| ::aidl::android::hardware::drm::KeySetId* _aidl_return) { |
| if (in_scope.size() == 0 || in_response.size() == 0) { |
| // Returns empty keySetId |
| *_aidl_return = {}; |
| return toNdkScopedAStatus(Status::BAD_VALUE); |
| } |
| |
| std::string responseString(reinterpret_cast<const char*>(in_response.data()), |
| in_response.size()); |
| const std::vector<uint8_t> scopeId = in_scope; |
| std::vector<uint8_t> sessionId = {}; |
| std::string keySetId; |
| |
| bool isOfflineLicense = responseString.find(kOfflineLicense) != std::string::npos; |
| if (scopeId.size() < kKeySetIdPrefix.size()) { |
| android_errorWriteLog(0x534e4554, "144507096"); |
| *_aidl_return = {}; |
| return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE); |
| } |
| bool isRelease = (memcmp(scopeId.data(), kKeySetIdPrefix.data(), kKeySetIdPrefix.size()) == 0); |
| if (isRelease) { |
| keySetId.assign(scopeId.begin(), scopeId.end()); |
| |
| auto iter = mReleaseKeysMap.find(std::string(keySetId.begin(), keySetId.end())); |
| if (iter != mReleaseKeysMap.end()) { |
| sessionId.assign(iter->second.begin(), iter->second.end()); |
| } |
| } else { |
| sessionId.assign(scopeId.begin(), scopeId.end()); |
| // non offline license returns empty keySetId |
| keySetId.clear(); |
| } |
| |
| ::android::sp<Session> session = mSessionLibrary->findSession(sessionId); |
| if (!session.get()) { |
| *_aidl_return = {}; |
| return toNdkScopedAStatus(Status::ERROR_DRM_SESSION_NOT_OPENED); |
| } |
| setPlayPolicy(); |
| |
| auto res = session->provideKeyResponse(in_response); |
| if (res == clearkeydrm::OK) { |
| if (isOfflineLicense) { |
| if (isRelease) { |
| mFileHandle.DeleteLicense(keySetId); |
| mSessionLibrary->destroySession(session); |
| } else { |
| if (!makeKeySetId(&keySetId)) { |
| *_aidl_return = {}; |
| return toNdkScopedAStatus(Status::ERROR_DRM_UNKNOWN); |
| } |
| |
| bool ok = mFileHandle.StoreLicense( |
| keySetId, DeviceFiles::kLicenseStateActive, |
| std::string(in_response.begin(), in_response.end())); |
| if (!ok) { |
| ALOGE("Failed to store offline license"); |
| } |
| } |
| } |
| |
| // Test calling AMediaDrm listeners. |
| sendEvent(EventType::VENDOR_DEFINED, sessionId, sessionId); |
| |
| sendExpirationUpdate(sessionId, 100); |
| |
| std::vector<KeyStatus> keysStatus = {}; |
| KeyStatus keyStatus; |
| |
| std::vector<uint8_t> keyId1 = {0xA, 0xB, 0xC}; |
| keyStatus.keyId = keyId1; |
| keyStatus.type = KeyStatusType::USABLE; |
| keysStatus.push_back(keyStatus); |
| |
| std::vector<uint8_t> keyId2 = {0xD, 0xE, 0xF}; |
| keyStatus.keyId = keyId2; |
| keyStatus.type = KeyStatusType::EXPIRED; |
| keysStatus.push_back(keyStatus); |
| |
| std::vector<uint8_t> keyId3 = {0x0, 0x1, 0x2}; |
| keyStatus.keyId = keyId3; |
| keyStatus.type = KeyStatusType::USABLE_IN_FUTURE; |
| keysStatus.push_back(keyStatus); |
| |
| sendKeysChange(sessionId, keysStatus, true); |
| |
| installSecureStop(sessionId); |
| } else { |
| ALOGE("provideKeyResponse returns error=%d", res); |
| } |
| |
| std::vector<uint8_t> keySetIdVec(keySetId.begin(), keySetId.end()); |
| _aidl_return->keySetId = keySetIdVec; |
| return toNdkScopedAStatus(res); |
| } |
| |
| ::ndk::ScopedAStatus DrmPlugin::provideProvisionResponse( |
| const std::vector<uint8_t>& in_response, |
| ::aidl::android::hardware::drm::ProvideProvisionResponseResult* _aidl_return) { |
| Status status = Status::ERROR_DRM_CANNOT_HANDLE; |
| _aidl_return->certificate = {}; |
| _aidl_return->wrappedKey = {}; |
| if (in_response.size() == 0) { |
| status = Status::BAD_VALUE; |
| } |
| return toNdkScopedAStatus(status); |
| } |
| |
| ::ndk::ScopedAStatus DrmPlugin::queryKeyStatus( |
| const std::vector<uint8_t>& in_sessionId, |
| std::vector<::aidl::android::hardware::drm::KeyValue>* _aidl_return) { |
| if (in_sessionId.size() == 0) { |
| // Returns empty key status KeyValue pair |
| *_aidl_return = {}; |
| return toNdkScopedAStatus(Status::BAD_VALUE); |
| } |
| |
| std::vector<KeyValue> infoMap = {}; |
| mPlayPolicyLock.lock(); |
| KeyValue keyValuePair; |
| for (size_t i = 0; i < mPlayPolicy.size(); ++i) { |
| keyValuePair.key = mPlayPolicy[i].key; |
| keyValuePair.value = mPlayPolicy[i].value; |
| infoMap.push_back(keyValuePair); |
| } |
| mPlayPolicyLock.unlock(); |
| *_aidl_return = infoMap; |
| return toNdkScopedAStatus(Status::OK); |
| } |
| |
| ::ndk::ScopedAStatus DrmPlugin::releaseAllSecureStops() { |
| Status status = Status::OK; |
| const auto res = removeAllSecureStops(); |
| if (!res.isOk() && res.getExceptionCode() == EX_SERVICE_SPECIFIC) { |
| status = static_cast<Status>(res.getServiceSpecificError()); |
| } |
| return toNdkScopedAStatus(status); |
| } |
| |
| ::ndk::ScopedAStatus DrmPlugin::releaseSecureStop( |
| const ::aidl::android::hardware::drm::SecureStopId& in_secureStopId) { |
| Status status = Status::OK; |
| const auto res = removeSecureStop(in_secureStopId); |
| if (!res.isOk() && res.getExceptionCode() == EX_SERVICE_SPECIFIC) { |
| status = static_cast<Status>(res.getServiceSpecificError()); |
| } |
| return toNdkScopedAStatus(status); |
| } |
| |
| ::ndk::ScopedAStatus DrmPlugin::releaseSecureStops( |
| const ::aidl::android::hardware::drm::OpaqueData& in_ssRelease) { |
| // OpaqueData starts with 4 byte decimal integer string |
| const size_t kFourBytesOffset = 4; |
| if (in_ssRelease.opaqueData.size() < kFourBytesOffset) { |
| ALOGE("Invalid secureStopRelease length"); |
| return toNdkScopedAStatus(Status::BAD_VALUE); |
| } |
| |
| Status status = Status::OK; |
| std::vector<uint8_t> input = in_ssRelease.opaqueData; |
| |
| if (input.size() < kSecureStopIdSize + kFourBytesOffset) { |
| // The minimum size of secure stop has to contain |
| // a 4 bytes count and one secureStop id |
| ALOGE("Total size of secureStops is too short"); |
| return toNdkScopedAStatus(Status::BAD_VALUE); |
| } |
| |
| // The format of opaqueData is shared between the server |
| // and the drm service. The clearkey implementation consists of: |
| // count - number of secure stops |
| // list of fixed length secure stops |
| size_t countBufferSize = sizeof(uint32_t); |
| if (input.size() < countBufferSize) { |
| // SafetyNet logging |
| android_errorWriteLog(0x534e4554, "144766455"); |
| return toNdkScopedAStatus(Status::BAD_VALUE); |
| } |
| uint32_t count = 0; |
| sscanf(reinterpret_cast<char*>(input.data()), "%04" PRIu32, &count); |
| |
| // Avoid divide by 0 below. |
| if (count == 0) { |
| ALOGE("Invalid 0 secureStop count"); |
| return toNdkScopedAStatus(Status::BAD_VALUE); |
| } |
| |
| // Computes the fixed length secureStop size |
| size_t secureStopSize = (input.size() - kFourBytesOffset) / count; |
| if (secureStopSize < kSecureStopIdSize) { |
| // A valid secureStop contains the id plus data |
| ALOGE("Invalid secureStop size"); |
| return toNdkScopedAStatus(Status::BAD_VALUE); |
| } |
| uint8_t* buffer = new uint8_t[secureStopSize]; |
| size_t offset = kFourBytesOffset; // skip the count |
| for (size_t i = 0; i < count; ++i, offset += secureStopSize) { |
| memcpy(buffer, input.data() + offset, secureStopSize); |
| |
| // A secureStop contains id+data, we only use the id for removal |
| std::vector<uint8_t> id(buffer, buffer + kSecureStopIdSize); |
| ::aidl::android::hardware::drm::SecureStopId secureStopId{id}; |
| const auto res = removeSecureStop(secureStopId); |
| if (!res.isOk() && res.getExceptionCode() == EX_SERVICE_SPECIFIC) { |
| status = static_cast<Status>(res.getServiceSpecificError()); |
| } |
| if (Status::OK != status) break; |
| } |
| |
| delete[] buffer; |
| return toNdkScopedAStatus(status); |
| } |
| |
| ::ndk::ScopedAStatus DrmPlugin::removeAllSecureStops() { |
| Mutex::Autolock lock(mSecureStopLock); |
| |
| mSecureStops.clear(); |
| mNextSecureStopId = kSecureStopIdStart; |
| return toNdkScopedAStatus(Status::OK); |
| } |
| |
| ::ndk::ScopedAStatus DrmPlugin::removeKeys(const std::vector<uint8_t>& in_sessionId) { |
| Status status = Status::ERROR_DRM_CANNOT_HANDLE; |
| if (in_sessionId.size() == 0) { |
| status = Status::BAD_VALUE; |
| } |
| return toNdkScopedAStatus(status); |
| } |
| |
| ::ndk::ScopedAStatus DrmPlugin::removeOfflineLicense( |
| const ::aidl::android::hardware::drm::KeySetId& in_keySetId) { |
| if (mMockError != Status::OK) { |
| return toNdkScopedAStatus(toMockStatus(mMockError)); |
| } |
| Status status = Status::BAD_VALUE; |
| std::string licenseName(in_keySetId.keySetId.begin(), in_keySetId.keySetId.end()); |
| if (mFileHandle.DeleteLicense(licenseName)) { |
| status = Status::OK; |
| } |
| return toNdkScopedAStatus(status); |
| } |
| |
| ::ndk::ScopedAStatus DrmPlugin::removeSecureStop( |
| const ::aidl::android::hardware::drm::SecureStopId& in_secureStopId) { |
| Mutex::Autolock lock(mSecureStopLock); |
| |
| Status status = Status::OK; |
| if (1 != mSecureStops.erase(in_secureStopId.secureStopId)) { |
| status = Status::BAD_VALUE; |
| } |
| return toNdkScopedAStatus(status); |
| } |
| |
| ::ndk::ScopedAStatus DrmPlugin::requiresSecureDecoder( |
| const std::string& in_mime, ::aidl::android::hardware::drm::SecurityLevel in_level, |
| bool* _aidl_return) { |
| UNUSED(in_mime); |
| UNUSED(in_level); |
| *_aidl_return = false; |
| return ::ndk::ScopedAStatus::ok(); |
| } |
| |
| ::ndk::ScopedAStatus DrmPlugin::restoreKeys( |
| const std::vector<uint8_t>& in_sessionId, |
| const ::aidl::android::hardware::drm::KeySetId& in_keySetId) { |
| if (in_sessionId.size() == 0 || in_keySetId.keySetId.size() == 0) { |
| return toNdkScopedAStatus(Status::BAD_VALUE); |
| } |
| |
| DeviceFiles::LicenseState licenseState; |
| std::string offlineLicense; |
| if (!mFileHandle.RetrieveLicense( |
| std::string(in_keySetId.keySetId.begin(), in_keySetId.keySetId.end()), |
| &licenseState, &offlineLicense)) { |
| ALOGE("Failed to restore offline license"); |
| return toNdkScopedAStatus(Status::ERROR_DRM_NO_LICENSE); |
| } |
| |
| if (DeviceFiles::kLicenseStateUnknown == licenseState || |
| DeviceFiles::kLicenseStateReleasing == licenseState) { |
| ALOGE("Invalid license state=%d", licenseState); |
| return toNdkScopedAStatus(Status::ERROR_DRM_NO_LICENSE); |
| } |
| |
| ::android::sp<Session> session = mSessionLibrary->findSession(in_sessionId); |
| if (!session.get()) { |
| return toNdkScopedAStatus(Status::ERROR_DRM_SESSION_NOT_OPENED); |
| } |
| auto res = session->provideKeyResponse( |
| std::vector<uint8_t>(offlineLicense.begin(), offlineLicense.end())); |
| if (res != clearkeydrm::OK) { |
| ALOGE("Failed to restore keys"); |
| } |
| return toNdkScopedAStatus(res); |
| } |
| |
| void DrmPlugin::sendEvent(::aidl::android::hardware::drm::EventType in_eventType, |
| const std::vector<uint8_t>& in_sessionId, |
| const std::vector<uint8_t>& in_data) { |
| if (mListener != nullptr) { |
| mListener->onEvent(in_eventType, in_sessionId, in_data); |
| } else { |
| ALOGE("Null event listener, event not sent"); |
| } |
| return; |
| } |
| |
| void DrmPlugin::sendExpirationUpdate(const std::vector<uint8_t>& in_sessionId, |
| int64_t in_expiryTimeInMS) { |
| if (mListener != nullptr) { |
| mListener->onExpirationUpdate(in_sessionId, in_expiryTimeInMS); |
| } else { |
| ALOGE("Null event listener, event not sent"); |
| } |
| return; |
| } |
| |
| void DrmPlugin::sendKeysChange( |
| const std::vector<uint8_t>& in_sessionId, |
| const std::vector<::aidl::android::hardware::drm::KeyStatus>& in_keyStatusList, |
| bool in_hasNewUsableKey) { |
| if (mListener != nullptr) { |
| mListener->onKeysChange(in_sessionId, in_keyStatusList, in_hasNewUsableKey); |
| } else { |
| ALOGE("Null event listener, event not sent"); |
| } |
| return; |
| } |
| |
| void DrmPlugin::sendSessionLostState(const std::vector<uint8_t>& in_sessionId) { |
| if (mListener != nullptr) { |
| mListener->onSessionLostState(in_sessionId); |
| } |
| return; |
| } |
| |
| ::ndk::ScopedAStatus DrmPlugin::setCipherAlgorithm(const std::vector<uint8_t>& /*in_sessionId*/, |
| const std::string& /*in_algorithm*/) { |
| return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE); |
| } |
| |
| ::ndk::ScopedAStatus DrmPlugin::setListener( |
| const std::shared_ptr< |
| ::aidl::android::hardware::drm::IDrmPluginListener>& in_listener) { |
| mListener = in_listener; |
| return toNdkScopedAStatus(Status::OK); |
| } |
| |
| ::ndk::ScopedAStatus DrmPlugin::setMacAlgorithm(const std::vector<uint8_t>& /*in_sessionId*/, |
| const std::string& /*in_algorithm*/) { |
| return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE); |
| } |
| |
| ::ndk::ScopedAStatus DrmPlugin::setPlaybackId(const std::vector<uint8_t>& in_sessionId, |
| const std::string& in_playbackId) { |
| if (in_sessionId.size() == 0) { |
| ALOGE("Invalid empty session id"); |
| return toNdkScopedAStatus(Status::BAD_VALUE); |
| } |
| |
| std::vector<uint8_t> sid = in_sessionId; |
| mPlaybackId[sid] = in_playbackId; |
| return toNdkScopedAStatus(Status::OK); |
| } |
| |
| ::ndk::ScopedAStatus DrmPlugin::setPropertyByteArray(const std::string& in_propertyName, |
| const std::vector<uint8_t>& in_value) { |
| if (in_propertyName == kDeviceIdKey) { |
| ALOGD("Cannot set immutable property: %s", in_propertyName.c_str()); |
| return toNdkScopedAStatus(Status::BAD_VALUE); |
| } else if (in_propertyName == kClientIdKey) { |
| mByteArrayProperties[kClientIdKey] = in_value; |
| return toNdkScopedAStatus(Status::OK); |
| } |
| |
| // Setting of undefined properties is not supported |
| ALOGE("Failed to set property byte array, key=%s", in_propertyName.c_str()); |
| return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE); |
| } |
| |
| ::ndk::ScopedAStatus DrmPlugin::setPropertyString(const std::string& in_propertyName, |
| const std::string& in_value) { |
| std::string immutableKeys; |
| immutableKeys.append(kAlgorithmsKey + ","); |
| immutableKeys.append(kPluginDescriptionKey + ","); |
| immutableKeys.append(kVendorKey + ","); |
| immutableKeys.append(kVersionKey + ","); |
| |
| std::string key = std::string(in_propertyName.c_str()); |
| if (immutableKeys.find(key) != std::string::npos) { |
| ALOGD("Cannot set immutable property: %s", key.c_str()); |
| return toNdkScopedAStatus(Status::BAD_VALUE); |
| } |
| |
| std::map<std::string, std::string>::iterator itr = mStringProperties.find(key); |
| if (itr == mStringProperties.end()) { |
| ALOGE("Cannot set undefined property string, key=%s", key.c_str()); |
| return toNdkScopedAStatus(Status::BAD_VALUE); |
| } |
| |
| if (in_propertyName == kDrmErrorTestKey) { |
| if (in_value == kResourceContentionValue) { |
| mMockError = Status::ERROR_DRM_RESOURCE_CONTENTION; |
| } else if (in_value == kLostStateValue) { |
| mMockError = Status::ERROR_DRM_SESSION_LOST_STATE; |
| } else if (in_value == kFrameTooLargeValue) { |
| mMockError = Status::ERROR_DRM_FRAME_TOO_LARGE; |
| } else if (in_value == kInvalidStateValue) { |
| mMockError = Status::ERROR_DRM_INVALID_STATE; |
| } else { |
| mMockError = Status::ERROR_DRM_UNKNOWN; |
| } |
| } |
| |
| mStringProperties[key] = std::string(in_value.c_str()); |
| return toNdkScopedAStatus(Status::OK); |
| } |
| |
| ::ndk::ScopedAStatus DrmPlugin::sign(const std::vector<uint8_t>& /*in_sessionId*/, |
| const std::vector<uint8_t>& /*in_keyId*/, |
| const std::vector<uint8_t>& /*in_message*/, |
| std::vector<uint8_t>* _aidl_return) { |
| *_aidl_return = {}; |
| return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE); |
| } |
| |
| ::ndk::ScopedAStatus DrmPlugin::signRSA(const std::vector<uint8_t>& /*in_sessionId*/, |
| const std::string& /*in_algorithm*/, |
| const std::vector<uint8_t>& /*in_message*/, |
| const std::vector<uint8_t>& /*in_wrappedkey*/, |
| std::vector<uint8_t>* _aidl_return) { |
| *_aidl_return = {}; |
| return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE); |
| } |
| |
| ::ndk::ScopedAStatus DrmPlugin::verify(const std::vector<uint8_t>& /*in_sessionId*/, |
| const std::vector<uint8_t>& /*in_keyId*/, |
| const std::vector<uint8_t>& /*in_message*/, |
| const std::vector<uint8_t>& /*in_signature*/, |
| bool* _aidl_return) { |
| *_aidl_return = false; |
| return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE); |
| } |
| |
| // Private methods below. |
| void DrmPlugin::setPlayPolicy() { |
| ::android::Mutex::Autolock lock(mPlayPolicyLock); |
| mPlayPolicy.clear(); |
| |
| KeyValue policy; |
| policy.key = kQueryKeyLicenseType; |
| policy.value = kStreaming; |
| mPlayPolicy.push_back(policy); |
| |
| policy.key = kQueryKeyPlayAllowed; |
| policy.value = kTrue; |
| mPlayPolicy.push_back(policy); |
| |
| policy.key = kQueryKeyRenewAllowed; |
| mPlayPolicy.push_back(policy); |
| } |
| |
| bool DrmPlugin::makeKeySetId(std::string* keySetId) { |
| if (!keySetId) { |
| ALOGE("keySetId destination not provided"); |
| return false; |
| } |
| std::vector<uint8_t> ksid(kKeySetIdPrefix.begin(), kKeySetIdPrefix.end()); |
| ksid.resize(kKeySetIdLength); |
| std::vector<uint8_t> randomData((kKeySetIdLength - kKeySetIdPrefix.size()) / 2, 0); |
| |
| while (keySetId->empty()) { |
| for (auto itr = randomData.begin(); itr != randomData.end(); ++itr) { |
| *itr = std::rand() % 0xff; |
| } |
| auto id = reinterpret_cast<const uint8_t*>(randomData.data()); |
| *keySetId = kKeySetIdPrefix + ::android::ByteArrayToHexString(id, randomData.size()); |
| if (mFileHandle.LicenseExists(*keySetId)) { |
| // collision, regenerate |
| ALOGV("Retry generating KeySetId"); |
| keySetId->clear(); |
| } |
| } |
| return true; |
| } |
| |
| Status DrmPlugin::setSecurityLevel(const std::vector<uint8_t>& sessionId, SecurityLevel level) { |
| if (sessionId.size() == 0) { |
| ALOGE("Invalid empty session id"); |
| return Status::BAD_VALUE; |
| } |
| |
| if (level != SecurityLevel::DEFAULT && level != SecurityLevel::SW_SECURE_CRYPTO) { |
| ALOGE("Cannot set security level > max"); |
| return Status::ERROR_DRM_CANNOT_HANDLE; |
| } |
| |
| std::vector<uint8_t> sid = sessionId; |
| ::android::sp<Session> session = mSessionLibrary->findSession(sid); |
| if (!session.get()) { |
| return Status::ERROR_DRM_SESSION_NOT_OPENED; |
| } |
| |
| Mutex::Autolock lock(mSecurityLevelLock); |
| std::map<std::vector<uint8_t>, SecurityLevel>::iterator itr = mSecurityLevel.find(sid); |
| if (itr != mSecurityLevel.end()) { |
| mSecurityLevel[sid] = level; |
| } else { |
| if (!mSecurityLevel.insert(std::pair<std::vector<uint8_t>, SecurityLevel>(sid, level)) |
| .second) { |
| ALOGE("Failed to set security level"); |
| return Status::ERROR_DRM_INVALID_STATE; |
| } |
| } |
| return Status::OK; |
| } |
| |
| } // namespace clearkey |
| } // namespace drm |
| } // namespace hardware |
| } // namespace android |
| } // namespace aidl |