widevine_cdm_component_installer.cc revision c2e0dbddbe15c98d52c4786dac06cb8952a8ae6d
1// Copyright (c) 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 "chrome/browser/component_updater/widevine_cdm_component_installer.h" 6 7#include <string.h> 8 9#include <vector> 10 11#include "base/base_paths.h" 12#include "base/bind.h" 13#include "base/command_line.h" 14#include "base/compiler_specific.h" 15#include "base/file_util.h" 16#include "base/files/file_path.h" 17#include "base/logging.h" 18#include "base/path_service.h" 19#include "base/string_util.h" 20#include "base/values.h" 21#include "base/version.h" 22#include "build/build_config.h" 23#include "chrome/browser/component_updater/component_updater_service.h" 24#include "chrome/browser/plugins/plugin_prefs.h" 25#include "chrome/common/chrome_constants.h" 26#include "chrome/common/chrome_paths.h" 27#include "chrome/common/widevine_cdm_constants.h" 28#include "content/public/browser/browser_thread.h" 29#include "content/public/browser/plugin_service.h" 30#include "content/public/common/pepper_plugin_info.h" 31#include "third_party/widevine/cdm/widevine_cdm_common.h" 32 33#include "widevine_cdm_version.h" // In SHARED_INTERMEDIATE_DIR. 34 35using content::BrowserThread; 36using content::PluginService; 37 38namespace { 39 40// TODO(xhwang): Move duplicate code among all component installer 41// implementations to some common place. 42 43#if defined(WIDEVINE_CDM_AVAILABLE) && defined(WIDEVINE_CDM_IS_COMPONENT) 44 45// CRX hash. The extension id is: pdkaonnflpjcgibpgaanildgengnihcm. 46const uint8 kSha2Hash[] = { 0xf3, 0xa0, 0xed, 0xd5, 0xbf, 0x92, 0x68, 0x1f, 47 0x60, 0x0d, 0x8b, 0x36, 0x4d, 0x6d, 0x87, 0x2c, 48 0x86, 0x61, 0x12, 0x20, 0x21, 0xf8, 0x94, 0xdd, 49 0xe1, 0xb6, 0xb4, 0x55, 0x34, 0x8c, 0x2e, 0x20 }; 50 51// File name of the Widevine CDM component manifest on different platforms. 52const char kWidevineCdmManifestName[] = "WidevineCdm"; 53 54// Name of the Widevine CDM OS in the component manifest. 55const char kWidevineCdmOperatingSystem[] = 56#if defined(OS_MACOSX) 57 "mac"; 58#elif defined(OS_WIN) 59 "win"; 60#else // OS_LINUX, etc. TODO(viettrungluu): Separate out Chrome OS and Android? 61 "linux"; 62#endif 63 64// Name of the Widevine CDM architecture in the component manifest. 65const char kWidevineCdmArch[] = 66#if defined(ARCH_CPU_X86) 67 "ia32"; 68#elif defined(ARCH_CPU_X86_64) 69 "x64"; 70#else // TODO(viettrungluu): Support an ARM check? 71 "???"; 72#endif 73 74// If we don't have a Widevine CDM component, this is the version we claim. 75const char kNullVersion[] = "0.0.0.0"; 76 77// The base directory on Windows looks like: 78// <profile>\AppData\Local\Google\Chrome\User Data\WidevineCdm\. 79base::FilePath GetWidevineCdmBaseDirectory() { 80 base::FilePath result; 81 PathService::Get(chrome::DIR_COMPONENT_WIDEVINE_CDM, &result); 82 return result; 83} 84 85// Widevine CDM has the version encoded in the path so we need to enumerate the 86// directories to find the full path. 87// On success, |latest_dir| returns something like: 88// <profile>\AppData\Local\Google\Chrome\User Data\WidevineCdm\10.3.44.555\. 89// |latest_version| returns the corresponding version number. |older_dirs| 90// returns directories of all older versions. 91bool GetWidevineCdmDirectory(base::FilePath* latest_dir, 92 base::Version* latest_version, 93 std::vector<base::FilePath>* older_dirs) { 94 base::FilePath base_dir = GetWidevineCdmBaseDirectory(); 95 bool found = false; 96 file_util::FileEnumerator file_enumerator( 97 base_dir, false, file_util::FileEnumerator::DIRECTORIES); 98 for (base::FilePath path = file_enumerator.Next(); !path.value().empty(); 99 path = file_enumerator.Next()) { 100 base::Version version(path.BaseName().MaybeAsASCII()); 101 if (!version.IsValid()) 102 continue; 103 if (found) { 104 if (version.CompareTo(*latest_version) > 0) { 105 older_dirs->push_back(*latest_dir); 106 *latest_dir = path; 107 *latest_version = version; 108 } else { 109 older_dirs->push_back(path); 110 } 111 } else { 112 *latest_dir = path; 113 *latest_version = version; 114 found = true; 115 } 116 } 117 return found; 118} 119 120bool MakeWidevineCdmPluginInfo(const base::FilePath& path, 121 const base::Version& version, 122 content::PepperPluginInfo* plugin_info) { 123 if (!version.IsValid() || 124 version.components().size() != 125 static_cast<size_t>(kWidevineCdmVersionNumComponents)) { 126 return false; 127 } 128 129 plugin_info->is_internal = false; 130 // Widevine CDM must run out of process. 131 plugin_info->is_out_of_process = true; 132 plugin_info->path = path; 133 plugin_info->name = kWidevineCdmDisplayName; 134 plugin_info->description = kWidevineCdmDescription; 135 plugin_info->version = version.GetString(); 136 webkit::WebPluginMimeType widevine_cdm_mime_type( 137 kWidevineCdmPluginMimeType, 138 kWidevineCdmPluginExtension, 139 kWidevineCdmPluginMimeTypeDescription); 140 plugin_info->mime_types.push_back(widevine_cdm_mime_type); 141 plugin_info->permissions = kWidevineCdmPluginPermissions; 142 143 return true; 144} 145 146void RegisterWidevineCdmWithChrome(const base::FilePath& path, 147 const base::Version& version) { 148 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 149 content::PepperPluginInfo plugin_info; 150 if (!MakeWidevineCdmPluginInfo(path, version, &plugin_info)) 151 return; 152 153 PluginService::GetInstance()->RegisterInternalPlugin( 154 plugin_info.ToWebPluginInfo(), true); 155 PluginService::GetInstance()->RefreshPlugins(); 156} 157 158// Returns true if this browser is compatible with the given Widevine CDM 159// manifest, with the version specified in the manifest in |version_out|. 160bool CheckWidevineCdmManifest(const base::DictionaryValue& manifest, 161 base::Version* version_out) { 162 std::string name; 163 manifest.GetStringASCII("name", &name); 164 165 if (name != kWidevineCdmManifestName) 166 return false; 167 168 std::string proposed_version; 169 manifest.GetStringASCII("version", &proposed_version); 170 base::Version version(proposed_version.c_str()); 171 if (!version.IsValid()) 172 return false; 173 174 std::string os; 175 manifest.GetStringASCII("x-widevine-cdm-os", &os); 176 if (os != kWidevineCdmOperatingSystem) 177 return false; 178 179 std::string arch; 180 manifest.GetStringASCII("x-widevine-cdm-arch", &arch); 181 if (arch != kWidevineCdmArch) 182 return false; 183 184 *version_out = version; 185 return true; 186} 187 188class WidevineCdmComponentInstaller : public ComponentInstaller { 189 public: 190 explicit WidevineCdmComponentInstaller(const base::Version& version); 191 virtual ~WidevineCdmComponentInstaller() {} 192 193 virtual void OnUpdateError(int error) OVERRIDE; 194 virtual bool Install(const base::DictionaryValue& manifest, 195 const base::FilePath& unpack_path) OVERRIDE; 196 197 private: 198 base::Version current_version_; 199}; 200 201WidevineCdmComponentInstaller::WidevineCdmComponentInstaller( 202 const base::Version& version) 203 : current_version_(version) { 204 DCHECK(version.IsValid()); 205} 206 207void WidevineCdmComponentInstaller::OnUpdateError(int error) { 208 NOTREACHED() << "Widevine CDM update error: " << error; 209} 210 211bool WidevineCdmComponentInstaller::Install( 212 const base::DictionaryValue& manifest, 213 const base::FilePath& unpack_path) { 214 base::Version version; 215 if (!CheckWidevineCdmManifest(manifest, &version)) 216 return false; 217 if (current_version_.CompareTo(version) > 0) 218 return false; 219 220 if (!file_util::PathExists(unpack_path.Append(kWidevineCdmFileName))) 221 return false; 222 223 base::FilePath adapter_source_path; 224 PathService::Get(chrome::FILE_WIDEVINE_CDM_ADAPTER, &adapter_source_path); 225 if (!file_util::PathExists(adapter_source_path)) 226 return false; 227 228 // Passed the basic tests. Time to install it. 229 base::FilePath install_path = 230 GetWidevineCdmBaseDirectory().AppendASCII(version.GetString()); 231 if (file_util::PathExists(install_path)) 232 return false; 233 if (!file_util::Move(unpack_path, install_path)) 234 return false; 235 236 base::FilePath adapter_install_path = 237 install_path.Append(kWidevineCdmAdapterFileName); 238 if (!file_util::CopyFile(adapter_source_path, adapter_install_path)) 239 return false; 240 241 // Installation is done. Now register the Widevine CDM with chrome. 242 current_version_ = version; 243 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind( 244 &RegisterWidevineCdmWithChrome, adapter_install_path, version)); 245 return true; 246} 247 248void FinishWidevineCdmUpdateRegistration(ComponentUpdateService* cus, 249 const base::Version& version) { 250 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 251 CrxComponent widevine_cdm; 252 widevine_cdm.name = "WidevineCdm"; 253 widevine_cdm.installer = new WidevineCdmComponentInstaller(version); 254 widevine_cdm.version = version; 255 widevine_cdm.pk_hash.assign(kSha2Hash, &kSha2Hash[sizeof(kSha2Hash)]); 256 if (cus->RegisterComponent(widevine_cdm) != ComponentUpdateService::kOk) { 257 NOTREACHED() << "Widevine CDM component registration failed."; 258 return; 259 } 260} 261 262void StartWidevineCdmUpdateRegistration(ComponentUpdateService* cus) { 263 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 264 base::FilePath path = GetWidevineCdmBaseDirectory(); 265 if (!file_util::PathExists(path) && !file_util::CreateDirectory(path)) { 266 NOTREACHED() << "Could not create Widevine CDM directory."; 267 return; 268 } 269 270 base::Version version(kNullVersion); 271 std::vector<base::FilePath> older_dirs; 272 if (GetWidevineCdmDirectory(&path, &version, &older_dirs)) { 273 if (file_util::PathExists(path.Append(kWidevineCdmAdapterFileName)) && 274 file_util::PathExists(path.Append(kWidevineCdmFileName))) { 275 BrowserThread::PostTask( 276 BrowserThread::UI, FROM_HERE, 277 base::Bind(&RegisterWidevineCdmWithChrome, path, version)); 278 } else { 279 file_util::Delete(path, true); 280 version = base::Version(kNullVersion); 281 } 282 } 283 284 BrowserThread::PostTask( 285 BrowserThread::UI, FROM_HERE, 286 base::Bind(&FinishWidevineCdmUpdateRegistration, cus, version)); 287 288 // Remove older versions of Widevine CDM. 289 for (std::vector<base::FilePath>::iterator iter = older_dirs.begin(); 290 iter != older_dirs.end(); ++iter) { 291 file_util::Delete(*iter, true); 292 } 293} 294 295#endif // defined(WIDEVINE_CDM_AVAILABLE) && defined(WIDEVINE_CDM_IS_COMPONENT) 296 297} // namespace 298 299void RegisterWidevineCdmComponent(ComponentUpdateService* cus) { 300#if defined(WIDEVINE_CDM_AVAILABLE) && defined(WIDEVINE_CDM_IS_COMPONENT) 301 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, 302 base::Bind(&StartWidevineCdmUpdateRegistration, cus)); 303#endif // defined(WIDEVINE_CDM_AVAILABLE) && defined(WIDEVINE_CDM_IS_COMPONENT) 304} 305