[M120 Migration][MM][CAPI] Fix the logic for media using capi player.
[platform/framework/web/chromium-efl.git] / media / mojo / services / mojo_cdm_file_io.cc
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.
4
5 #include "media/mojo/services/mojo_cdm_file_io.h"
6
7 #include <utility>
8
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"
15
16 namespace media {
17
18 namespace {
19
20 using ClientStatus = cdm::FileIOClient::Status;
21 using FileStatus = media::mojom::CdmFile::Status;
22 using StorageStatus = media::mojom::CdmStorage::Status;
23
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;
27
28 const char* ConvertStorageStatus(StorageStatus status) {
29   switch (status) {
30     case StorageStatus::kSuccess:
31       return "kSuccess";
32     case StorageStatus::kInUse:
33       return "kInUse";
34     case StorageStatus::kFailure:
35       return "kFailure";
36   }
37
38   return "unknown";
39 }
40
41 const char* ConvertFileStatus(FileStatus status) {
42   return status == FileStatus::kSuccess ? "kSuccess" : "kFailure";
43 }
44
45 }  // namespace
46
47 MojoCdmFileIO::MojoCdmFileIO(Delegate* delegate,
48                              cdm::FileIOClient* client,
49                              mojo::Remote<mojom::CdmStorage> cdm_storage)
50     : delegate_(delegate),
51       client_(client),
52       cdm_storage_(std::move(cdm_storage)) {
53   DVLOG(1) << __func__;
54   DCHECK(delegate_);
55   DCHECK(client_);
56   DCHECK(cdm_storage_);
57 }
58
59 MojoCdmFileIO::~MojoCdmFileIO() {
60   DVLOG(1) << __func__;
61 }
62
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;
66
67   // Open is only allowed if the current state is kUnopened and the file name
68   // is valid.
69   if (state_ != State::kUnopened) {
70     OnError(ErrorType::kOpenError);
71     return;
72   }
73
74   state_ = State::kOpening;
75   file_name_ = file_name_string;
76
77   TRACE_EVENT_ASYNC_BEGIN1("media", "MojoCdmFileIO::Open", this, "file_name",
78                            file_name_);
79
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
83   // Open() failed.
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));
88 }
89
90 void MojoCdmFileIO::OnFileOpened(
91     StorageStatus status,
92     mojo::PendingAssociatedRemote<mojom::CdmFile> cdm_file) {
93   DVLOG(3) << __func__ << " file: " << file_name_ << ", status: " << status;
94
95   UMA_HISTOGRAM_ENUMERATION("Media.EME.CdmFileIO::OpenFile", status);
96
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));
101   switch (status) {
102     case StorageStatus::kSuccess:
103       // File was successfully opened.
104       state_ = State::kOpened;
105       cdm_file_.Bind(std::move(cdm_file));
106       {
107         TRACE_EVENT0("media", "FileIOClient::OnOpenComplete");
108         client_->OnOpenComplete(ClientStatus::kSuccess);
109       }
110       return;
111     case StorageStatus::kInUse:
112       // File already open by somebody else.
113       state_ = State::kUnopened;
114       OnError(ErrorType::kOpenInUse);
115       return;
116     case StorageStatus::kFailure:
117       // Something went wrong.
118       state_ = State::kError;
119       OnError(ErrorType::kOpenError);
120       return;
121   }
122
123   NOTREACHED_NORETURN();
124 }
125
126 void MojoCdmFileIO::Read() {
127   DVLOG(3) << __func__ << " file: " << file_name_;
128
129   // If another operation is in progress, fail.
130   if (state_ == State::kReading || state_ == State::kWriting) {
131     OnError(ErrorType::kReadInUse);
132     return;
133   }
134
135   // If the file is not open, fail.
136   if (state_ != State::kOpened) {
137     OnError(ErrorType::kReadError);
138     return;
139   }
140
141   TRACE_EVENT_ASYNC_BEGIN1("media", "MojoCdmFileIO::Read", this, "file_name",
142                            file_name_);
143
144   state_ = State::kReading;
145
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
149   // Read() failed.
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));
154 }
155
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_);
160
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));
165
166   if (status != FileStatus::kSuccess) {
167     DVLOG(1) << "Failed to read file " << file_name_;
168     state_ = State::kOpened;
169     OnError(ErrorType::kReadError);
170     return;
171   }
172
173   // Call this before OnReadComplete() so that we always have the latest file
174   // size before CDM fires errors.
175   delegate_->ReportFileReadSize(data.size());
176
177   state_ = State::kOpened;
178   TRACE_EVENT0("media", "FileIOClient::OnReadComplete");
179   client_->OnReadComplete(ClientStatus::kSuccess, data.data(), data.size());
180 }
181
182 void MojoCdmFileIO::Write(const uint8_t* data, uint32_t data_size) {
183   DVLOG(3) << __func__ << " file: " << file_name_ << ", bytes: " << data_size;
184
185   // If another operation is in progress, fail.
186   if (state_ == State::kReading || state_ == State::kWriting) {
187     OnError(ErrorType::kWriteInUse);
188     return;
189   }
190
191   // If the file is not open, fail.
192   if (state_ != State::kOpened) {
193     OnError(ErrorType::kWriteError);
194     return;
195   }
196
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);
202     return;
203   }
204
205   TRACE_EVENT_ASYNC_BEGIN2("media", "MojoCdmFileIO::Write", this, "file_name",
206                            file_name_, "bytes_to_write", data_size);
207
208   state_ = State::kWriting;
209
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
213   // Write() failed.
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));
219 }
220
221 void MojoCdmFileIO::OnFileWritten(FileStatus status) {
222   DVLOG(3) << __func__ << " file: " << file_name_;
223   DCHECK_EQ(State::kWriting, state_);
224
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));
229
230   if (status != FileStatus::kSuccess) {
231     DVLOG(1) << "Failed to write file " << file_name_;
232     state_ = State::kError;
233     OnError(ErrorType::kWriteError);
234     return;
235   }
236
237   state_ = State::kOpened;
238   TRACE_EVENT0("media", "FileIOClient::OnWriteComplete");
239   client_->OnWriteComplete(ClientStatus::kSuccess);
240 }
241
242 void MojoCdmFileIO::Close() {
243   DVLOG(3) << __func__ << " file: " << file_name_;
244
245   // Note: |this| could be deleted as part of this call.
246   delegate_->CloseCdmFileIO(this);
247 }
248
249 void MojoCdmFileIO::OnError(ErrorType error) {
250   DVLOG(3) << __func__ << " file: " << file_name_ << ", error: " << (int)error;
251
252   base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
253       FROM_HERE, base::BindOnce(&MojoCdmFileIO::NotifyClientOfError,
254                                 weak_factory_.GetWeakPtr(), error));
255 }
256
257 void MojoCdmFileIO::NotifyClientOfError(ErrorType error) {
258   // Note that no event tracing is done for error conditions.
259   switch (error) {
260     case ErrorType::kOpenError:
261       client_->OnOpenComplete(ClientStatus::kError);
262       break;
263     case ErrorType::kOpenInUse:
264       client_->OnOpenComplete(ClientStatus::kInUse);
265       break;
266     case ErrorType::kReadError:
267       client_->OnReadComplete(ClientStatus::kError, nullptr, 0);
268       break;
269     case ErrorType::kReadInUse:
270       client_->OnReadComplete(ClientStatus::kInUse, nullptr, 0);
271       break;
272     case ErrorType::kWriteError:
273       client_->OnWriteComplete(ClientStatus::kError);
274       break;
275     case ErrorType::kWriteInUse:
276       client_->OnWriteComplete(ClientStatus::kInUse);
277       break;
278   }
279 }
280
281 }  // namespace media