| /* |
| * Copyright (C) 2018 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_NDEBUG 0 |
| #define LOG_TAG "hidl_ClearKeyPlugin" |
| #include <utils/Log.h> |
| |
| #include <stdio.h> |
| #include <inttypes.h> |
| |
| #include "DrmPlugin.h" |
| #include "ClearKeyDrmProperties.h" |
| #include "Session.h" |
| #include "TypeConvert.h" |
| |
| namespace { |
| const int kSecureStopIdStart = 100; |
| const std::string kStreaming("Streaming"); |
| const std::string kOffline("Offline"); |
| 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 android { |
| namespace hardware { |
| namespace drm { |
| namespace V1_1 { |
| namespace clearkey { |
| |
| DrmPlugin::DrmPlugin(SessionLibrary* sessionLibrary) |
| : mSessionLibrary(sessionLibrary), |
| mOpenSessionOkCount(0), |
| mCloseSessionOkCount(0), |
| mCloseSessionNotOpenedCount(0), |
| mNextSecureStopId(kSecureStopIdStart) { |
| mPlayPolicy.clear(); |
| initProperties(); |
| mSecureStops.clear(); |
| } |
| |
| void DrmPlugin::initProperties() { |
| mStringProperties.clear(); |
| mStringProperties[kVendorKey] = kVendorValue; |
| mStringProperties[kVersionKey] = kVersionValue; |
| mStringProperties[kPluginDescriptionKey] = kPluginDescriptionValue; |
| mStringProperties[kAlgorithmsKey] = kAlgorithmsValue; |
| mStringProperties[kListenerTestSupportKey] = kListenerTestSupportValue; |
| |
| 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 hidl_vec<uint8_t>& sessionId) { |
| 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)); |
| } |
| |
| Return<void> DrmPlugin::openSession(openSession_cb _hidl_cb) { |
| sp<Session> session = mSessionLibrary->createSession(); |
| std::vector<uint8_t> sessionId = session->sessionId(); |
| |
| Status status = setSecurityLevel(sessionId, SecurityLevel::SW_SECURE_CRYPTO); |
| _hidl_cb(status, toHidlVec(sessionId)); |
| mOpenSessionOkCount++; |
| return Void(); |
| } |
| |
| Return<void> DrmPlugin::openSession_1_1(SecurityLevel securityLevel, |
| openSession_1_1_cb _hidl_cb) { |
| sp<Session> session = mSessionLibrary->createSession(); |
| std::vector<uint8_t> sessionId = session->sessionId(); |
| |
| Status status = setSecurityLevel(sessionId, securityLevel); |
| _hidl_cb(status, toHidlVec(sessionId)); |
| mOpenSessionOkCount++; |
| return Void(); |
| } |
| |
| Return<Status> DrmPlugin::closeSession(const hidl_vec<uint8_t>& sessionId) { |
| if (sessionId.size() == 0) { |
| return Status::BAD_VALUE; |
| } |
| |
| sp<Session> session = mSessionLibrary->findSession(toVector(sessionId)); |
| if (session.get()) { |
| mCloseSessionOkCount++; |
| mSessionLibrary->destroySession(session); |
| return Status::OK; |
| } |
| mCloseSessionNotOpenedCount++; |
| return Status::ERROR_DRM_SESSION_NOT_OPENED; |
| } |
| |
| Status DrmPlugin::getKeyRequestCommon(const hidl_vec<uint8_t>& scope, |
| const hidl_vec<uint8_t>& initData, |
| const hidl_string& mimeType, |
| KeyType keyType, |
| const hidl_vec<KeyValue>& optionalParameters, |
| std::vector<uint8_t> *request, |
| KeyRequestType *keyRequestType, |
| std::string *defaultUrl) { |
| UNUSED(optionalParameters); |
| |
| *defaultUrl = ""; |
| *keyRequestType = KeyRequestType::UNKNOWN; |
| *request = std::vector<uint8_t>(); |
| |
| if (scope.size() == 0) { |
| return Status::BAD_VALUE; |
| } |
| |
| if (keyType != KeyType::STREAMING) { |
| return Status::ERROR_DRM_CANNOT_HANDLE; |
| } |
| |
| sp<Session> session = mSessionLibrary->findSession(toVector(scope)); |
| if (!session.get()) { |
| return Status::ERROR_DRM_SESSION_NOT_OPENED; |
| } |
| |
| Status status = session->getKeyRequest(initData, mimeType, request); |
| *keyRequestType = KeyRequestType::INITIAL; |
| return status; |
| } |
| |
| Return<void> DrmPlugin::getKeyRequest( |
| const hidl_vec<uint8_t>& scope, |
| const hidl_vec<uint8_t>& initData, |
| const hidl_string& mimeType, |
| KeyType keyType, |
| const hidl_vec<KeyValue>& optionalParameters, |
| getKeyRequest_cb _hidl_cb) { |
| UNUSED(optionalParameters); |
| |
| KeyRequestType keyRequestType = KeyRequestType::UNKNOWN; |
| std::string defaultUrl(""); |
| std::vector<uint8_t> request; |
| Status status = getKeyRequestCommon( |
| scope, initData, mimeType, keyType, optionalParameters, |
| &request, &keyRequestType, &defaultUrl); |
| |
| _hidl_cb(status, toHidlVec(request), |
| static_cast<drm::V1_0::KeyRequestType>(keyRequestType), |
| hidl_string(defaultUrl)); |
| return Void(); |
| } |
| |
| Return<void> DrmPlugin::getKeyRequest_1_1( |
| const hidl_vec<uint8_t>& scope, |
| const hidl_vec<uint8_t>& initData, |
| const hidl_string& mimeType, |
| KeyType keyType, |
| const hidl_vec<KeyValue>& optionalParameters, |
| getKeyRequest_1_1_cb _hidl_cb) { |
| UNUSED(optionalParameters); |
| |
| KeyRequestType keyRequestType = KeyRequestType::UNKNOWN; |
| std::string defaultUrl(""); |
| std::vector<uint8_t> request; |
| Status status = getKeyRequestCommon( |
| scope, initData, mimeType, keyType, optionalParameters, |
| &request, &keyRequestType, &defaultUrl); |
| |
| _hidl_cb(status, toHidlVec(request), keyRequestType, hidl_string(defaultUrl)); |
| return Void(); |
| } |
| |
| void DrmPlugin::setPlayPolicy() { |
| 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); |
| } |
| |
| Return<void> DrmPlugin::provideKeyResponse( |
| const hidl_vec<uint8_t>& scope, |
| const hidl_vec<uint8_t>& response, |
| provideKeyResponse_cb _hidl_cb) { |
| if (scope.size() == 0 || response.size() == 0) { |
| // Returns empty keySetId |
| _hidl_cb(Status::BAD_VALUE, hidl_vec<uint8_t>()); |
| return Void(); |
| } |
| |
| sp<Session> session = mSessionLibrary->findSession(toVector(scope)); |
| if (!session.get()) { |
| _hidl_cb(Status::ERROR_DRM_SESSION_NOT_OPENED, hidl_vec<uint8_t>()); |
| return Void(); |
| } |
| |
| setPlayPolicy(); |
| std::vector<uint8_t> keySetId; |
| Status status = session->provideKeyResponse(response); |
| if (status == Status::OK) { |
| // This is for testing AMediaDrm_setOnEventListener only. |
| sendEvent(EventType::VENDOR_DEFINED, 0, scope); |
| keySetId.clear(); |
| } |
| |
| installSecureStop(scope); |
| |
| // Returns status and empty keySetId |
| _hidl_cb(status, toHidlVec(keySetId)); |
| return Void(); |
| } |
| |
| Return<void> DrmPlugin::getPropertyString( |
| const hidl_string& propertyName, getPropertyString_cb _hidl_cb) { |
| std::string name(propertyName.c_str()); |
| std::string value; |
| |
| 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 { |
| ALOGE("App requested unknown string property %s", name.c_str()); |
| _hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, ""); |
| return Void(); |
| } |
| _hidl_cb(Status::OK, value.c_str()); |
| return Void(); |
| } |
| |
| Return<void> DrmPlugin::getPropertyByteArray( |
| const hidl_string& propertyName, getPropertyByteArray_cb _hidl_cb) { |
| std::map<std::string, std::vector<uint8_t> >::iterator itr = |
| mByteArrayProperties.find(std::string(propertyName.c_str())); |
| if (itr == mByteArrayProperties.end()) { |
| ALOGE("App requested unknown property: %s", propertyName.c_str()); |
| _hidl_cb(Status::BAD_VALUE, std::vector<uint8_t>()); |
| return Void(); |
| } |
| _hidl_cb(Status::OK, itr->second); |
| return Void(); |
| |
| } |
| |
| Return<Status> DrmPlugin::setPropertyString( |
| const hidl_string& name, const hidl_string& value) { |
| std::string immutableKeys; |
| immutableKeys.append(kAlgorithmsKey + ","); |
| immutableKeys.append(kPluginDescriptionKey + ","); |
| immutableKeys.append(kVendorKey + ","); |
| immutableKeys.append(kVersionKey + ","); |
| |
| std::string key = std::string(name.c_str()); |
| if (immutableKeys.find(key) != std::string::npos) { |
| ALOGD("Cannot set immutable property: %s", key.c_str()); |
| return 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 Status::BAD_VALUE; |
| } |
| |
| mStringProperties[key] = std::string(value.c_str()); |
| return Status::OK; |
| } |
| |
| Return<Status> DrmPlugin::setPropertyByteArray( |
| const hidl_string& name, const hidl_vec<uint8_t>& value) { |
| UNUSED(value); |
| if (name == kDeviceIdKey) { |
| ALOGD("Cannot set immutable property: %s", name.c_str()); |
| return Status::BAD_VALUE; |
| } |
| |
| // Setting of undefined properties is not supported |
| ALOGE("Failed to set property byte array, key=%s", name.c_str()); |
| return Status::ERROR_DRM_CANNOT_HANDLE; |
| } |
| |
| Return<void> DrmPlugin::queryKeyStatus( |
| const hidl_vec<uint8_t>& sessionId, |
| queryKeyStatus_cb _hidl_cb) { |
| |
| if (sessionId.size() == 0) { |
| // Returns empty key status KeyValue pair |
| _hidl_cb(Status::BAD_VALUE, hidl_vec<KeyValue>()); |
| return Void(); |
| } |
| |
| std::vector<KeyValue> infoMapVec; |
| infoMapVec.clear(); |
| |
| KeyValue keyValuePair; |
| for (size_t i = 0; i < mPlayPolicy.size(); ++i) { |
| keyValuePair.key = mPlayPolicy[i].key; |
| keyValuePair.value = mPlayPolicy[i].value; |
| infoMapVec.push_back(keyValuePair); |
| } |
| _hidl_cb(Status::OK, toHidlVec(infoMapVec)); |
| return Void(); |
| } |
| |
| Return<void> DrmPlugin::getNumberOfSessions(getNumberOfSessions_cb _hidl_cb) { |
| uint32_t currentSessions = mSessionLibrary->numOpenSessions(); |
| uint32_t maxSessions = 10; |
| _hidl_cb(Status::OK, currentSessions, maxSessions); |
| return Void(); |
| } |
| |
| Return<void> DrmPlugin::getSecurityLevel(const hidl_vec<uint8_t>& sessionId, |
| getSecurityLevel_cb _hidl_cb) { |
| if (sessionId.size() == 0) { |
| _hidl_cb(Status::BAD_VALUE, SecurityLevel::UNKNOWN); |
| return Void(); |
| } |
| |
| std::vector<uint8_t> sid = toVector(sessionId); |
| sp<Session> session = mSessionLibrary->findSession(sid); |
| if (!session.get()) { |
| _hidl_cb(Status::ERROR_DRM_SESSION_NOT_OPENED, SecurityLevel::UNKNOWN); |
| return Void(); |
| } |
| |
| std::map<std::vector<uint8_t>, SecurityLevel>::iterator itr = |
| mSecurityLevel.find(sid); |
| if (itr == mSecurityLevel.end()) { |
| ALOGE("Session id not found"); |
| _hidl_cb(Status::ERROR_DRM_INVALID_STATE, SecurityLevel::UNKNOWN); |
| return Void(); |
| } |
| |
| _hidl_cb(Status::OK, itr->second); |
| return Void(); |
| } |
| |
| Return<Status> DrmPlugin::setSecurityLevel(const hidl_vec<uint8_t>& sessionId, |
| SecurityLevel level) { |
| if (sessionId.size() == 0) { |
| ALOGE("Invalid empty session id"); |
| return Status::BAD_VALUE; |
| } |
| |
| if (level > SecurityLevel::SW_SECURE_CRYPTO) { |
| ALOGE("Cannot set security level > max"); |
| return Status::ERROR_DRM_CANNOT_HANDLE; |
| } |
| |
| std::vector<uint8_t> sid = toVector(sessionId); |
| sp<Session> session = mSessionLibrary->findSession(sid); |
| if (!session.get()) { |
| return Status::ERROR_DRM_SESSION_NOT_OPENED; |
| } |
| |
| 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; |
| } |
| |
| Return<void> DrmPlugin::getMetrics(getMetrics_cb _hidl_cb) { |
| // Set the open session count metric. |
| DrmMetricGroup::Attribute openSessionOkAttribute = { |
| "status", DrmMetricGroup::ValueType::INT64_TYPE, (int64_t) Status::OK, 0.0, "" |
| }; |
| DrmMetricGroup::Value openSessionMetricValue = { |
| "count", DrmMetricGroup::ValueType::INT64_TYPE, mOpenSessionOkCount, 0.0, "" |
| }; |
| DrmMetricGroup::Metric openSessionMetric = { |
| "open_session", { openSessionOkAttribute }, { openSessionMetricValue } |
| }; |
| |
| // Set the close session count metric. |
| DrmMetricGroup::Attribute closeSessionOkAttribute = { |
| "status", DrmMetricGroup::ValueType::INT64_TYPE, (int64_t) Status::OK, 0.0, "" |
| }; |
| DrmMetricGroup::Value closeSessionMetricValue = { |
| "count", DrmMetricGroup::ValueType::INT64_TYPE, mCloseSessionOkCount, 0.0, "" |
| }; |
| DrmMetricGroup::Metric closeSessionMetric = { |
| "close_session", { closeSessionOkAttribute }, { closeSessionMetricValue } |
| }; |
| |
| // Set the close session, not opened metric. |
| DrmMetricGroup::Attribute closeSessionNotOpenedAttribute = { |
| "status", DrmMetricGroup::ValueType::INT64_TYPE, |
| (int64_t) Status::ERROR_DRM_SESSION_NOT_OPENED, 0.0, "" |
| }; |
| DrmMetricGroup::Value closeSessionNotOpenedMetricValue = { |
| "count", DrmMetricGroup::ValueType::INT64_TYPE, mCloseSessionNotOpenedCount, 0.0, "" |
| }; |
| DrmMetricGroup::Metric closeSessionNotOpenedMetric = { |
| "close_session", { closeSessionNotOpenedAttribute }, { closeSessionNotOpenedMetricValue } |
| }; |
| |
| DrmMetricGroup metrics = { { openSessionMetric, closeSessionMetric, |
| closeSessionNotOpenedMetric } }; |
| |
| _hidl_cb(Status::OK, hidl_vec<DrmMetricGroup>({metrics})); |
| return Void(); |
| } |
| |
| Return<void> DrmPlugin::getSecureStops(getSecureStops_cb _hidl_cb) { |
| std::vector<SecureStop> stops; |
| for (auto itr = mSecureStops.begin(); itr != mSecureStops.end(); ++itr) { |
| ClearkeySecureStop clearkeyStop = itr->second; |
| std::vector<uint8_t> stopVec; |
| stopVec.insert(stopVec.end(), clearkeyStop.id.begin(), clearkeyStop.id.end()); |
| stopVec.insert(stopVec.end(), clearkeyStop.data.begin(), clearkeyStop.data.end()); |
| |
| SecureStop stop; |
| stop.opaqueData = toHidlVec(stopVec); |
| stops.push_back(stop); |
| } |
| _hidl_cb(Status::OK, stops); |
| return Void(); |
| } |
| |
| Return<void> DrmPlugin::getSecureStop(const hidl_vec<uint8_t>& secureStopId, |
| getSecureStop_cb _hidl_cb) { |
| SecureStop stop; |
| auto itr = mSecureStops.find(toVector(secureStopId)); |
| if (itr != mSecureStops.end()) { |
| ClearkeySecureStop clearkeyStop = itr->second; |
| std::vector<uint8_t> stopVec; |
| stopVec.insert(stopVec.end(), clearkeyStop.id.begin(), clearkeyStop.id.end()); |
| stopVec.insert(stopVec.end(), clearkeyStop.data.begin(), clearkeyStop.data.end()); |
| |
| stop.opaqueData = toHidlVec(stopVec); |
| _hidl_cb(Status::OK, stop); |
| } else { |
| _hidl_cb(Status::BAD_VALUE, stop); |
| } |
| |
| return Void(); |
| } |
| |
| Return<Status> DrmPlugin::releaseSecureStop(const hidl_vec<uint8_t>& secureStopId) { |
| return removeSecureStop(secureStopId); |
| } |
| |
| Return<Status> DrmPlugin::releaseAllSecureStops() { |
| return removeAllSecureStops(); |
| } |
| |
| Return<void> DrmPlugin::getSecureStopIds(getSecureStopIds_cb _hidl_cb) { |
| std::vector<SecureStopId> ids; |
| for (auto itr = mSecureStops.begin(); itr != mSecureStops.end(); ++itr) { |
| ids.push_back(itr->first); |
| } |
| |
| _hidl_cb(Status::OK, toHidlVec(ids)); |
| return Void(); |
| } |
| |
| Return<Status> DrmPlugin::releaseSecureStops(const SecureStopRelease& ssRelease) { |
| if (ssRelease.opaqueData.size() == 0) { |
| return Status::BAD_VALUE; |
| } |
| |
| Status status = Status::OK; |
| std::vector<uint8_t> input = toVector(ssRelease.opaqueData); |
| |
| // 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 Status::BAD_VALUE; |
| } |
| uint32_t count = 0; |
| sscanf(reinterpret_cast<char*>(input.data()), "%04" PRIu32, &count); |
| |
| // Avoid divide by 0 below. |
| if (count == 0) { |
| return Status::BAD_VALUE; |
| } |
| |
| size_t secureStopSize = (input.size() - countBufferSize) / count; |
| uint8_t buffer[secureStopSize]; |
| size_t offset = countBufferSize; // skip the count |
| for (size_t i = 0; i < count; ++i, offset += secureStopSize) { |
| memcpy(buffer, input.data() + offset, secureStopSize); |
| std::vector<uint8_t> id(buffer, buffer + kSecureStopIdSize); |
| |
| status = removeSecureStop(toHidlVec(id)); |
| if (Status::OK != status) break; |
| } |
| |
| return status; |
| } |
| |
| Return<Status> DrmPlugin::removeSecureStop(const hidl_vec<uint8_t>& secureStopId) { |
| if (1 != mSecureStops.erase(toVector(secureStopId))) { |
| return Status::BAD_VALUE; |
| } |
| return Status::OK; |
| } |
| |
| Return<Status> DrmPlugin::removeAllSecureStops() { |
| mSecureStops.clear(); |
| mNextSecureStopId = kSecureStopIdStart; |
| return Status::OK; |
| } |
| |
| } // namespace clearkey |
| } // namespace V1_1 |
| } // namespace drm |
| } // namespace hardware |
| } // namespace android |