| OLD | NEW | 
 | (Empty) | 
|    1 // Copyright 2013 The Chromium Authors. All rights reserved. |  | 
|    2 // Use of this source code is governed by a BSD-style license that can be |  | 
|    3 // found in the LICENSE file. |  | 
|    4  |  | 
|    5 #include "media/cdm/proxy_decryptor.h" |  | 
|    6  |  | 
|    7 #include <stddef.h> |  | 
|    8 #include <cstring> |  | 
|    9 #include <utility> |  | 
|   10  |  | 
|   11 #include "base/bind.h" |  | 
|   12 #include "base/callback_helpers.h" |  | 
|   13 #include "base/logging.h" |  | 
|   14 #include "base/macros.h" |  | 
|   15 #include "base/strings/string_util.h" |  | 
|   16 #include "build/build_config.h" |  | 
|   17 #include "media/base/cdm_callback_promise.h" |  | 
|   18 #include "media/base/cdm_config.h" |  | 
|   19 #include "media/base/cdm_factory.h" |  | 
|   20 #include "media/base/cdm_key_information.h" |  | 
|   21 #include "media/base/key_systems.h" |  | 
|   22 #include "media/base/media_permission.h" |  | 
|   23 #include "media/cdm/json_web_key.h" |  | 
|   24 #include "media/cdm/key_system_names.h" |  | 
|   25  |  | 
|   26 namespace media { |  | 
|   27  |  | 
|   28 // Special system code to signal a closed persistent session in a SessionError() |  | 
|   29 // call. This is needed because there is no SessionClosed() call in the prefixed |  | 
|   30 // EME API. |  | 
|   31 const int kSessionClosedSystemCode = 29127; |  | 
|   32  |  | 
|   33 ProxyDecryptor::PendingGenerateKeyRequestData::PendingGenerateKeyRequestData( |  | 
|   34     EmeInitDataType init_data_type, |  | 
|   35     const std::vector<uint8_t>& init_data) |  | 
|   36     : init_data_type(init_data_type), init_data(init_data) {} |  | 
|   37  |  | 
|   38 ProxyDecryptor::PendingGenerateKeyRequestData:: |  | 
|   39     ~PendingGenerateKeyRequestData() { |  | 
|   40 } |  | 
|   41  |  | 
|   42 ProxyDecryptor::ProxyDecryptor(MediaPermission* media_permission, |  | 
|   43                                bool use_hw_secure_codecs, |  | 
|   44                                const KeyAddedCB& key_added_cb, |  | 
|   45                                const KeyErrorCB& key_error_cb, |  | 
|   46                                const KeyMessageCB& key_message_cb) |  | 
|   47     : is_creating_cdm_(false), |  | 
|   48 #if defined(OS_CHROMEOS) || defined(OS_ANDROID) |  | 
|   49       media_permission_(media_permission), |  | 
|   50 #endif |  | 
|   51       use_hw_secure_codecs_(use_hw_secure_codecs), |  | 
|   52       key_added_cb_(key_added_cb), |  | 
|   53       key_error_cb_(key_error_cb), |  | 
|   54       key_message_cb_(key_message_cb), |  | 
|   55       is_clear_key_(false), |  | 
|   56       weak_ptr_factory_(this) { |  | 
|   57   DCHECK(media_permission); |  | 
|   58   DCHECK(!key_added_cb_.is_null()); |  | 
|   59   DCHECK(!key_error_cb_.is_null()); |  | 
|   60   DCHECK(!key_message_cb_.is_null()); |  | 
|   61 } |  | 
|   62  |  | 
|   63 ProxyDecryptor::~ProxyDecryptor() { |  | 
|   64   // Destroy the decryptor explicitly before destroying the plugin. |  | 
|   65   media_keys_ = nullptr; |  | 
|   66 } |  | 
|   67  |  | 
|   68 void ProxyDecryptor::CreateCdm(CdmFactory* cdm_factory, |  | 
|   69                                const std::string& key_system, |  | 
|   70                                const GURL& security_origin, |  | 
|   71                                const CdmContextReadyCB& cdm_context_ready_cb) { |  | 
|   72   DVLOG(1) << __FUNCTION__ << ": key_system = " << key_system; |  | 
|   73   DCHECK(!is_creating_cdm_); |  | 
|   74   DCHECK(!media_keys_); |  | 
|   75  |  | 
|   76   // TODO(sandersd): Trigger permissions check here and use it to determine |  | 
|   77   // distinctive identifier support, instead of always requiring the |  | 
|   78   // permission. http://crbug.com/455271 |  | 
|   79   CdmConfig cdm_config; |  | 
|   80   cdm_config.allow_distinctive_identifier = true; |  | 
|   81   cdm_config.allow_persistent_state = true; |  | 
|   82   cdm_config.use_hw_secure_codecs = use_hw_secure_codecs_; |  | 
|   83  |  | 
|   84   is_creating_cdm_ = true; |  | 
|   85  |  | 
|   86   base::WeakPtr<ProxyDecryptor> weak_this = weak_ptr_factory_.GetWeakPtr(); |  | 
|   87   cdm_factory->Create( |  | 
|   88       key_system, security_origin, cdm_config, |  | 
|   89       base::Bind(&ProxyDecryptor::OnSessionMessage, weak_this), |  | 
|   90       base::Bind(&ProxyDecryptor::OnSessionClosed, weak_this), |  | 
|   91       base::Bind(&ProxyDecryptor::OnLegacySessionError, weak_this), |  | 
|   92       base::Bind(&ProxyDecryptor::OnSessionKeysChange, weak_this), |  | 
|   93       base::Bind(&ProxyDecryptor::OnSessionExpirationUpdate, weak_this), |  | 
|   94       base::Bind(&ProxyDecryptor::OnCdmCreated, weak_this, key_system, |  | 
|   95                  security_origin, cdm_context_ready_cb)); |  | 
|   96 } |  | 
|   97  |  | 
|   98 void ProxyDecryptor::OnCdmCreated(const std::string& key_system, |  | 
|   99                                   const GURL& security_origin, |  | 
|  100                                   const CdmContextReadyCB& cdm_context_ready_cb, |  | 
|  101                                   const scoped_refptr<MediaKeys>& cdm, |  | 
|  102                                   const std::string& /* error_message */) { |  | 
|  103   is_creating_cdm_ = false; |  | 
|  104  |  | 
|  105   if (!cdm) { |  | 
|  106     cdm_context_ready_cb.Run(nullptr); |  | 
|  107   } else { |  | 
|  108     key_system_ = key_system; |  | 
|  109     security_origin_ = security_origin; |  | 
|  110     is_clear_key_ = IsClearKey(key_system) || IsExternalClearKey(key_system); |  | 
|  111     media_keys_ = cdm; |  | 
|  112  |  | 
|  113     cdm_context_ready_cb.Run(media_keys_->GetCdmContext()); |  | 
|  114   } |  | 
|  115  |  | 
|  116   for (const auto& request : pending_requests_) |  | 
|  117     GenerateKeyRequestInternal(request->init_data_type, request->init_data); |  | 
|  118  |  | 
|  119   pending_requests_.clear(); |  | 
|  120 } |  | 
|  121  |  | 
|  122 void ProxyDecryptor::GenerateKeyRequest(EmeInitDataType init_data_type, |  | 
|  123                                         const uint8_t* init_data, |  | 
|  124                                         int init_data_length) { |  | 
|  125   std::vector<uint8_t> init_data_vector(init_data, |  | 
|  126                                         init_data + init_data_length); |  | 
|  127  |  | 
|  128   if (is_creating_cdm_) { |  | 
|  129     pending_requests_.push_back( |  | 
|  130         new PendingGenerateKeyRequestData(init_data_type, init_data_vector)); |  | 
|  131     return; |  | 
|  132   } |  | 
|  133  |  | 
|  134   GenerateKeyRequestInternal(init_data_type, init_data_vector); |  | 
|  135 } |  | 
|  136  |  | 
|  137 // Returns true if |data| is prefixed with |header| and has data after the |  | 
|  138 // |header|. |  | 
|  139 static bool HasHeader(const std::vector<uint8_t>& data, |  | 
|  140                       const std::string& header) { |  | 
|  141   return data.size() > header.size() && |  | 
|  142          std::equal(header.begin(), header.end(), data.begin()); |  | 
|  143 } |  | 
|  144  |  | 
|  145 // Removes the first |length| items from |data|. |  | 
|  146 static void StripHeader(std::vector<uint8_t>& data, size_t length) { |  | 
|  147   data.erase(data.begin(), data.begin() + length); |  | 
|  148 } |  | 
|  149  |  | 
|  150 void ProxyDecryptor::GenerateKeyRequestInternal( |  | 
|  151     EmeInitDataType init_data_type, |  | 
|  152     const std::vector<uint8_t>& init_data) { |  | 
|  153   DVLOG(1) << __FUNCTION__; |  | 
|  154   DCHECK(!is_creating_cdm_); |  | 
|  155  |  | 
|  156   if (!media_keys_) { |  | 
|  157     OnLegacySessionError(std::string(), MediaKeys::NOT_SUPPORTED_ERROR, 0, |  | 
|  158                          "CDM creation failed."); |  | 
|  159     return; |  | 
|  160   } |  | 
|  161  |  | 
|  162   const char kPrefixedApiPersistentSessionHeader[] = "PERSISTENT|"; |  | 
|  163   const char kPrefixedApiLoadSessionHeader[] = "LOAD_SESSION|"; |  | 
|  164  |  | 
|  165   SessionCreationType session_creation_type = TemporarySession; |  | 
|  166   std::vector<uint8_t> stripped_init_data = init_data; |  | 
|  167   if (HasHeader(init_data, kPrefixedApiLoadSessionHeader)) { |  | 
|  168     session_creation_type = LoadSession; |  | 
|  169     StripHeader(stripped_init_data, strlen(kPrefixedApiLoadSessionHeader)); |  | 
|  170   } else if (HasHeader(init_data, kPrefixedApiPersistentSessionHeader)) { |  | 
|  171     session_creation_type = PersistentSession; |  | 
|  172     StripHeader(stripped_init_data, |  | 
|  173                 strlen(kPrefixedApiPersistentSessionHeader)); |  | 
|  174   } |  | 
|  175  |  | 
|  176   scoped_ptr<NewSessionCdmPromise> promise(new CdmCallbackPromise<std::string>( |  | 
|  177       base::Bind(&ProxyDecryptor::SetSessionId, weak_ptr_factory_.GetWeakPtr(), |  | 
|  178                  session_creation_type), |  | 
|  179       base::Bind(&ProxyDecryptor::OnLegacySessionError, |  | 
|  180                  weak_ptr_factory_.GetWeakPtr(), |  | 
|  181                  std::string())));  // No session id until created. |  | 
|  182  |  | 
|  183   if (session_creation_type == LoadSession) { |  | 
|  184     media_keys_->LoadSession( |  | 
|  185         MediaKeys::PERSISTENT_LICENSE_SESSION, |  | 
|  186         std::string(reinterpret_cast<const char*>(stripped_init_data.data()), |  | 
|  187                     stripped_init_data.size()), |  | 
|  188         std::move(promise)); |  | 
|  189     return; |  | 
|  190   } |  | 
|  191  |  | 
|  192   MediaKeys::SessionType session_type = |  | 
|  193       session_creation_type == PersistentSession |  | 
|  194           ? MediaKeys::PERSISTENT_LICENSE_SESSION |  | 
|  195           : MediaKeys::TEMPORARY_SESSION; |  | 
|  196  |  | 
|  197   // No permission required when AesDecryptor is used or when the key system is |  | 
|  198   // external clear key. |  | 
|  199   DCHECK(!key_system_.empty()); |  | 
|  200   if (CanUseAesDecryptor(key_system_) || IsExternalClearKey(key_system_)) { |  | 
|  201     OnPermissionStatus(session_type, init_data_type, stripped_init_data, |  | 
|  202                        std::move(promise), true /* granted */); |  | 
|  203     return; |  | 
|  204   } |  | 
|  205  |  | 
|  206 #if defined(OS_CHROMEOS) || defined(OS_ANDROID) |  | 
|  207   media_permission_->RequestPermission( |  | 
|  208       MediaPermission::PROTECTED_MEDIA_IDENTIFIER, security_origin_, |  | 
|  209       base::Bind(&ProxyDecryptor::OnPermissionStatus, |  | 
|  210                  weak_ptr_factory_.GetWeakPtr(), session_type, init_data_type, |  | 
|  211                  stripped_init_data, base::Passed(&promise))); |  | 
|  212 #else |  | 
|  213   OnPermissionStatus(session_type, init_data_type, stripped_init_data, |  | 
|  214                      std::move(promise), true /* granted */); |  | 
|  215 #endif |  | 
|  216 } |  | 
|  217  |  | 
|  218 void ProxyDecryptor::OnPermissionStatus( |  | 
|  219     MediaKeys::SessionType session_type, |  | 
|  220     EmeInitDataType init_data_type, |  | 
|  221     const std::vector<uint8_t>& init_data, |  | 
|  222     scoped_ptr<NewSessionCdmPromise> promise, |  | 
|  223     bool granted) { |  | 
|  224   // ProxyDecryptor is only used by Prefixed EME, where RequestPermission() is |  | 
|  225   // only for triggering the permission UI. Later CheckPermission() will be |  | 
|  226   // called (e.g. in PlatformVerificationFlow on ChromeOS; in BrowserCdmManager |  | 
|  227   // on Android) and the permission status will be evaluated then. |  | 
|  228   DVLOG_IF(1, !granted) << "Permission request rejected."; |  | 
|  229  |  | 
|  230   media_keys_->CreateSessionAndGenerateRequest(session_type, init_data_type, |  | 
|  231                                                init_data, std::move(promise)); |  | 
|  232 } |  | 
|  233  |  | 
|  234 void ProxyDecryptor::AddKey(const uint8_t* key, |  | 
|  235                             int key_length, |  | 
|  236                             const uint8_t* init_data, |  | 
|  237                             int init_data_length, |  | 
|  238                             const std::string& session_id) { |  | 
|  239   DVLOG(1) << "AddKey()"; |  | 
|  240  |  | 
|  241   if (!media_keys_) { |  | 
|  242     OnLegacySessionError(std::string(), MediaKeys::INVALID_STATE_ERROR, 0, |  | 
|  243                          "CDM is not available."); |  | 
|  244     return; |  | 
|  245   } |  | 
|  246  |  | 
|  247   // In the prefixed API, the session parameter provided to addKey() is |  | 
|  248   // optional, so use the single existing session if it exists. |  | 
|  249   std::string new_session_id(session_id); |  | 
|  250   if (new_session_id.empty()) { |  | 
|  251     if (active_sessions_.size() == 1) { |  | 
|  252       base::hash_map<std::string, bool>::iterator it = active_sessions_.begin(); |  | 
|  253       new_session_id = it->first; |  | 
|  254     } else { |  | 
|  255       OnLegacySessionError(std::string(), MediaKeys::NOT_SUPPORTED_ERROR, 0, |  | 
|  256                            "SessionId not specified."); |  | 
|  257       return; |  | 
|  258     } |  | 
|  259   } |  | 
|  260  |  | 
|  261   scoped_ptr<SimpleCdmPromise> promise(new CdmCallbackPromise<>( |  | 
|  262       base::Bind(&ProxyDecryptor::GenerateKeyAdded, |  | 
|  263                  weak_ptr_factory_.GetWeakPtr(), session_id), |  | 
|  264       base::Bind(&ProxyDecryptor::OnLegacySessionError, |  | 
|  265                  weak_ptr_factory_.GetWeakPtr(), session_id))); |  | 
|  266  |  | 
|  267   // EME WD spec only supports a single array passed to the CDM. For |  | 
|  268   // Clear Key using v0.1b, both arrays are used (|init_data| is key_id). |  | 
|  269   // Since the EME WD spec supports the key as a JSON Web Key, |  | 
|  270   // convert the 2 arrays to a JWK and pass it as the single array. |  | 
|  271   if (is_clear_key_) { |  | 
|  272     // Decryptor doesn't support empty key ID (see http://crbug.com/123265). |  | 
|  273     // So ensure a non-empty value is passed. |  | 
|  274     if (!init_data) { |  | 
|  275       static const uint8_t kDummyInitData[1] = {0}; |  | 
|  276       init_data = kDummyInitData; |  | 
|  277       init_data_length = arraysize(kDummyInitData); |  | 
|  278     } |  | 
|  279  |  | 
|  280     std::string jwk = |  | 
|  281         GenerateJWKSet(key, key_length, init_data, init_data_length); |  | 
|  282     DCHECK(!jwk.empty()); |  | 
|  283     media_keys_->UpdateSession(new_session_id, |  | 
|  284                                std::vector<uint8_t>(jwk.begin(), jwk.end()), |  | 
|  285                                std::move(promise)); |  | 
|  286     return; |  | 
|  287   } |  | 
|  288  |  | 
|  289   media_keys_->UpdateSession(new_session_id, |  | 
|  290                              std::vector<uint8_t>(key, key + key_length), |  | 
|  291                              std::move(promise)); |  | 
|  292 } |  | 
|  293  |  | 
|  294 void ProxyDecryptor::CancelKeyRequest(const std::string& session_id) { |  | 
|  295   DVLOG(1) << "CancelKeyRequest()"; |  | 
|  296  |  | 
|  297   if (!media_keys_) { |  | 
|  298     OnLegacySessionError(std::string(), MediaKeys::INVALID_STATE_ERROR, 0, |  | 
|  299                          "CDM is not available."); |  | 
|  300     return; |  | 
|  301   } |  | 
|  302  |  | 
|  303   scoped_ptr<SimpleCdmPromise> promise(new CdmCallbackPromise<>( |  | 
|  304       base::Bind(&ProxyDecryptor::OnSessionClosed, |  | 
|  305                  weak_ptr_factory_.GetWeakPtr(), session_id), |  | 
|  306       base::Bind(&ProxyDecryptor::OnLegacySessionError, |  | 
|  307                  weak_ptr_factory_.GetWeakPtr(), session_id))); |  | 
|  308   media_keys_->RemoveSession(session_id, std::move(promise)); |  | 
|  309 } |  | 
|  310  |  | 
|  311 void ProxyDecryptor::OnSessionMessage(const std::string& session_id, |  | 
|  312                                       MediaKeys::MessageType message_type, |  | 
|  313                                       const std::vector<uint8_t>& message, |  | 
|  314                                       const GURL& legacy_destination_url) { |  | 
|  315   // Assumes that OnSessionCreated() has been called before this. |  | 
|  316  |  | 
|  317   // For ClearKey, convert the message from JSON into just passing the key |  | 
|  318   // as the message. If unable to extract the key, return the message unchanged. |  | 
|  319   if (is_clear_key_) { |  | 
|  320     std::vector<uint8_t> key; |  | 
|  321     if (ExtractFirstKeyIdFromLicenseRequest(message, &key)) { |  | 
|  322       key_message_cb_.Run(session_id, key, legacy_destination_url); |  | 
|  323       return; |  | 
|  324     } |  | 
|  325   } |  | 
|  326  |  | 
|  327   key_message_cb_.Run(session_id, message, legacy_destination_url); |  | 
|  328 } |  | 
|  329  |  | 
|  330 void ProxyDecryptor::OnSessionKeysChange(const std::string& session_id, |  | 
|  331                                          bool has_additional_usable_key, |  | 
|  332                                          CdmKeysInfo keys_info) { |  | 
|  333   // EME v0.1b doesn't support this event. |  | 
|  334 } |  | 
|  335  |  | 
|  336 void ProxyDecryptor::OnSessionExpirationUpdate( |  | 
|  337     const std::string& session_id, |  | 
|  338     const base::Time& new_expiry_time) { |  | 
|  339   // EME v0.1b doesn't support this event. |  | 
|  340 } |  | 
|  341  |  | 
|  342 void ProxyDecryptor::GenerateKeyAdded(const std::string& session_id) { |  | 
|  343   // EME WD doesn't support this event, but it is needed for EME v0.1b. |  | 
|  344   key_added_cb_.Run(session_id); |  | 
|  345 } |  | 
|  346  |  | 
|  347 void ProxyDecryptor::OnSessionClosed(const std::string& session_id) { |  | 
|  348   base::hash_map<std::string, bool>::iterator it = |  | 
|  349       active_sessions_.find(session_id); |  | 
|  350  |  | 
|  351   // Latest EME spec separates closing a session ("allows an application to |  | 
|  352   // indicate that it no longer needs the session") and actually closing the |  | 
|  353   // session (done by the CDM at any point "such as in response to a close() |  | 
|  354   // call, when the session is no longer needed, or when system resources are |  | 
|  355   // lost.") Thus the CDM may cause 2 close() events -- one to resolve the |  | 
|  356   // close() promise, and a second to actually close the session. Prefixed EME |  | 
|  357   // only expects 1 close event, so drop the second (and subsequent) events. |  | 
|  358   // However, this means we can't tell if the CDM is generating spurious close() |  | 
|  359   // events. |  | 
|  360   if (it == active_sessions_.end()) |  | 
|  361     return; |  | 
|  362  |  | 
|  363   if (it->second) { |  | 
|  364     OnLegacySessionError(session_id, MediaKeys::NOT_SUPPORTED_ERROR, |  | 
|  365                          kSessionClosedSystemCode, |  | 
|  366                          "Do not close persistent sessions."); |  | 
|  367   } |  | 
|  368   active_sessions_.erase(it); |  | 
|  369 } |  | 
|  370  |  | 
|  371 void ProxyDecryptor::OnLegacySessionError(const std::string& session_id, |  | 
|  372                                           MediaKeys::Exception exception_code, |  | 
|  373                                           uint32_t system_code, |  | 
|  374                                           const std::string& error_message) { |  | 
|  375   // Convert |error_name| back to MediaKeys::KeyError if possible. Prefixed |  | 
|  376   // EME has different error message, so all the specific error events will |  | 
|  377   // get lost. |  | 
|  378   MediaKeys::KeyError error_code; |  | 
|  379   switch (exception_code) { |  | 
|  380     case MediaKeys::CLIENT_ERROR: |  | 
|  381       error_code = MediaKeys::kClientError; |  | 
|  382       break; |  | 
|  383     case MediaKeys::OUTPUT_ERROR: |  | 
|  384       error_code = MediaKeys::kOutputError; |  | 
|  385       break; |  | 
|  386     default: |  | 
|  387       // This will include all other CDM4 errors and any error generated |  | 
|  388       // by CDM5 or later. |  | 
|  389       error_code = MediaKeys::kUnknownError; |  | 
|  390       break; |  | 
|  391   } |  | 
|  392   key_error_cb_.Run(session_id, error_code, system_code); |  | 
|  393 } |  | 
|  394  |  | 
|  395 void ProxyDecryptor::SetSessionId(SessionCreationType session_type, |  | 
|  396                                   const std::string& session_id) { |  | 
|  397   // LoadSession() returns empty |session_id| if the session is not found, so |  | 
|  398   // convert this into an error. |  | 
|  399   if (session_type == LoadSession && session_id.empty()) { |  | 
|  400     OnLegacySessionError(session_id, MediaKeys::INVALID_ACCESS_ERROR, 0, |  | 
|  401                          "Incorrect session id specified for LoadSession()."); |  | 
|  402     return; |  | 
|  403   } |  | 
|  404  |  | 
|  405   // Loaded sessions are considered persistent. |  | 
|  406   bool is_persistent = |  | 
|  407       session_type == PersistentSession || session_type == LoadSession; |  | 
|  408   active_sessions_.insert(std::make_pair(session_id, is_persistent)); |  | 
|  409  |  | 
|  410   // For LoadSession(), generate the KeyAdded event. |  | 
|  411   if (session_type == LoadSession) |  | 
|  412     GenerateKeyAdded(session_id); |  | 
|  413 } |  | 
|  414  |  | 
|  415 }  // namespace media |  | 
| OLD | NEW |