1 // Copyright 2017 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "media/mojo/services/mojo_cdm_file_io.h"
9 #include "base/functional/bind.h"
10 #include "base/functional/callback.h"
11 #include "base/metrics/histogram_macros.h"
12 #include "base/task/single_thread_task_runner.h"
13 #include "base/trace_event/trace_event.h"
14 #include "mojo/public/cpp/bindings/callback_helpers.h"
20 using ClientStatus = cdm::FileIOClient::Status;
21 using FileStatus = media::mojom::CdmFile::Status;
22 using StorageStatus = media::mojom::CdmStorage::Status;
24 // File size limit is 512KB. Licenses saved by the CDM are typically several
25 // hundreds of bytes. This value should match what is in CdmFileImpl.
26 const int64_t kMaxFileSizeBytes = 512 * 1024;
28 const char* ConvertStorageStatus(StorageStatus status) {
30 case StorageStatus::kSuccess:
32 case StorageStatus::kInUse:
34 case StorageStatus::kFailure:
41 const char* ConvertFileStatus(FileStatus status) {
42 return status == FileStatus::kSuccess ? "kSuccess" : "kFailure";
47 MojoCdmFileIO::MojoCdmFileIO(Delegate* delegate,
48 cdm::FileIOClient* client,
49 mojo::Remote<mojom::CdmStorage> cdm_storage)
50 : delegate_(delegate),
52 cdm_storage_(std::move(cdm_storage)) {
59 MojoCdmFileIO::~MojoCdmFileIO() {
63 void MojoCdmFileIO::Open(const char* file_name, uint32_t file_name_size) {
64 std::string file_name_string(file_name, file_name_size);
65 DVLOG(3) << __func__ << " file: " << file_name_string;
67 // Open is only allowed if the current state is kUnopened and the file name
69 if (state_ != State::kUnopened) {
70 OnError(ErrorType::kOpenError);
74 state_ = State::kOpening;
75 file_name_ = file_name_string;
77 TRACE_EVENT_ASYNC_BEGIN1("media", "MojoCdmFileIO::Open", this, "file_name",
80 // Wrap the callback to detect the case when the mojo connection is
81 // terminated prior to receiving the response. This avoids problems if the
82 // service is destroyed before the CDM. If that happens let the CDM know that
84 auto callback = mojo::WrapCallbackWithDefaultInvokeIfNotRun(
85 base::BindOnce(&MojoCdmFileIO::OnFileOpened, weak_factory_.GetWeakPtr()),
86 StorageStatus::kFailure, mojo::NullAssociatedRemote());
87 cdm_storage_->Open(file_name_string, std::move(callback));
90 void MojoCdmFileIO::OnFileOpened(
92 mojo::PendingAssociatedRemote<mojom::CdmFile> cdm_file) {
93 DVLOG(3) << __func__ << " file: " << file_name_ << ", status: " << status;
95 UMA_HISTOGRAM_ENUMERATION("Media.EME.CdmFileIO::OpenFile", status);
97 // This logs the end of the async Open() request, and separately logs
98 // how long the client takes in OnOpenComplete().
99 TRACE_EVENT_ASYNC_END1("media", "MojoCdmFileIO::Open", this, "status",
100 ConvertStorageStatus(status));
102 case StorageStatus::kSuccess:
103 // File was successfully opened.
104 state_ = State::kOpened;
105 cdm_file_.Bind(std::move(cdm_file));
107 TRACE_EVENT0("media", "FileIOClient::OnOpenComplete");
108 client_->OnOpenComplete(ClientStatus::kSuccess);
111 case StorageStatus::kInUse:
112 // File already open by somebody else.
113 state_ = State::kUnopened;
114 OnError(ErrorType::kOpenInUse);
116 case StorageStatus::kFailure:
117 // Something went wrong.
118 state_ = State::kError;
119 OnError(ErrorType::kOpenError);
123 NOTREACHED_NORETURN();
126 void MojoCdmFileIO::Read() {
127 DVLOG(3) << __func__ << " file: " << file_name_;
129 // If another operation is in progress, fail.
130 if (state_ == State::kReading || state_ == State::kWriting) {
131 OnError(ErrorType::kReadInUse);
135 // If the file is not open, fail.
136 if (state_ != State::kOpened) {
137 OnError(ErrorType::kReadError);
141 TRACE_EVENT_ASYNC_BEGIN1("media", "MojoCdmFileIO::Read", this, "file_name",
144 state_ = State::kReading;
146 // Wrap the callback to detect the case when the mojo connection is
147 // terminated prior to receiving the response. This avoids problems if the
148 // service is destroyed before the CDM. If that happens let the CDM know that
150 auto callback = mojo::WrapCallbackWithDefaultInvokeIfNotRun(
151 base::BindOnce(&MojoCdmFileIO::OnFileRead, weak_factory_.GetWeakPtr()),
152 FileStatus::kFailure, std::vector<uint8_t>());
153 cdm_file_->Read(std::move(callback));
156 void MojoCdmFileIO::OnFileRead(FileStatus status,
157 const std::vector<uint8_t>& data) {
158 DVLOG(3) << __func__ << " file: " << file_name_;
159 DCHECK_EQ(State::kReading, state_);
161 // This logs the end of the async Read() request, and separately logs
162 // how long the client takes in OnReadComplete().
163 TRACE_EVENT_ASYNC_END2("media", "MojoCdmFileIO::Read", this, "bytes_read",
164 data.size(), "status", ConvertFileStatus(status));
166 if (status != FileStatus::kSuccess) {
167 DVLOG(1) << "Failed to read file " << file_name_;
168 state_ = State::kOpened;
169 OnError(ErrorType::kReadError);
173 // Call this before OnReadComplete() so that we always have the latest file
174 // size before CDM fires errors.
175 delegate_->ReportFileReadSize(data.size());
177 state_ = State::kOpened;
178 TRACE_EVENT0("media", "FileIOClient::OnReadComplete");
179 client_->OnReadComplete(ClientStatus::kSuccess, data.data(), data.size());
182 void MojoCdmFileIO::Write(const uint8_t* data, uint32_t data_size) {
183 DVLOG(3) << __func__ << " file: " << file_name_ << ", bytes: " << data_size;
185 // If another operation is in progress, fail.
186 if (state_ == State::kReading || state_ == State::kWriting) {
187 OnError(ErrorType::kWriteInUse);
191 // If the file is not open, fail.
192 if (state_ != State::kOpened) {
193 OnError(ErrorType::kWriteError);
197 // Files are limited in size, so fail if file too big.
198 if (data_size > kMaxFileSizeBytes) {
199 DLOG(WARNING) << __func__
200 << " Too much data to write. #bytes = " << data_size;
201 OnError(ErrorType::kWriteError);
205 TRACE_EVENT_ASYNC_BEGIN2("media", "MojoCdmFileIO::Write", this, "file_name",
206 file_name_, "bytes_to_write", data_size);
208 state_ = State::kWriting;
210 // Wrap the callback to detect the case when the mojo connection is
211 // terminated prior to receiving the response. This avoids problems if the
212 // service is destroyed before the CDM. If that happens let the CDM know that
214 auto callback = mojo::WrapCallbackWithDefaultInvokeIfNotRun(
215 base::BindOnce(&MojoCdmFileIO::OnFileWritten, weak_factory_.GetWeakPtr()),
216 FileStatus::kFailure);
217 cdm_file_->Write(std::vector<uint8_t>(data, data + data_size),
218 std::move(callback));
221 void MojoCdmFileIO::OnFileWritten(FileStatus status) {
222 DVLOG(3) << __func__ << " file: " << file_name_;
223 DCHECK_EQ(State::kWriting, state_);
225 // This logs the end of the async Write() request, and separately logs
226 // how long the client takes in OnWriteComplete().
227 TRACE_EVENT_ASYNC_END1("media", "MojoCdmFileIO::Write", this, "status",
228 ConvertFileStatus(status));
230 if (status != FileStatus::kSuccess) {
231 DVLOG(1) << "Failed to write file " << file_name_;
232 state_ = State::kError;
233 OnError(ErrorType::kWriteError);
237 state_ = State::kOpened;
238 TRACE_EVENT0("media", "FileIOClient::OnWriteComplete");
239 client_->OnWriteComplete(ClientStatus::kSuccess);
242 void MojoCdmFileIO::Close() {
243 DVLOG(3) << __func__ << " file: " << file_name_;
245 // Note: |this| could be deleted as part of this call.
246 delegate_->CloseCdmFileIO(this);
249 void MojoCdmFileIO::OnError(ErrorType error) {
250 DVLOG(3) << __func__ << " file: " << file_name_ << ", error: " << (int)error;
252 base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
253 FROM_HERE, base::BindOnce(&MojoCdmFileIO::NotifyClientOfError,
254 weak_factory_.GetWeakPtr(), error));
257 void MojoCdmFileIO::NotifyClientOfError(ErrorType error) {
258 // Note that no event tracing is done for error conditions.
260 case ErrorType::kOpenError:
261 client_->OnOpenComplete(ClientStatus::kError);
263 case ErrorType::kOpenInUse:
264 client_->OnOpenComplete(ClientStatus::kInUse);
266 case ErrorType::kReadError:
267 client_->OnReadComplete(ClientStatus::kError, nullptr, 0);
269 case ErrorType::kReadInUse:
270 client_->OnReadComplete(ClientStatus::kInUse, nullptr, 0);
272 case ErrorType::kWriteError:
273 client_->OnWriteComplete(ClientStatus::kError);
275 case ErrorType::kWriteInUse:
276 client_->OnWriteComplete(ClientStatus::kInUse);