Commit aa05b9c7 authored by John Rummell's avatar John Rummell Committed by Commit Bot

media: Add Mojo CDM File I/O implementation

Adds a cdm::FileIO implementation that can be used by mojo CDMs to read and
write files. Files are obtained from the mojo CdmStorage interface.

BUG=479923,776911
TEST=updated browser_test and new unit tests pass

Change-Id: Ib150058407d6db1d0ba2cabf4d5eb3f4a051232c
Reviewed-on: https://chromium-review.googlesource.com/706211Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Reviewed-by: default avatarBo <boliu@chromium.org>
Reviewed-by: default avatarXiaohan Wang <xhwang@chromium.org>
Commit-Queue: John Rummell <jrummell@chromium.org>
Cr-Commit-Position: refs/heads/master@{#512456}
parent 3e1e0633
......@@ -770,12 +770,6 @@ IN_PROC_BROWSER_TEST_P(ECKEncryptedMediaTest, CDMExpectedCrash) {
}
IN_PROC_BROWSER_TEST_P(ECKEncryptedMediaTest, FileIOTest) {
// TODO(jrummell): Support file IO in mojo CDM. See http://crbug.com/479923
if (IsUsingMojoCdm()) {
DVLOG(0) << "Skipping test; Not working with mojo CDM yet.";
return;
}
TestNonPlaybackCases(kExternalClearKeyFileIOTestKeySystem, kUnitTestSuccess);
}
......@@ -807,22 +801,10 @@ IN_PROC_BROWSER_TEST_P(ECKEncryptedMediaTest, Renewal) {
}
IN_PROC_BROWSER_TEST_P(ECKEncryptedMediaTest, LoadLoadableSession) {
// TODO(jrummell): Support file IO in mojo CDM. See http://crbug.com/479923
if (IsUsingMojoCdm()) {
DVLOG(0) << "Skipping test; Not working with mojo CDM yet.";
return;
}
TestPlaybackCase(kExternalClearKeyKeySystem, kLoadableSession, kEnded);
}
IN_PROC_BROWSER_TEST_P(ECKEncryptedMediaTest, LoadUnknownSession) {
// TODO(jrummell): Support file IO in mojo CDM. See http://crbug.com/479923
if (IsUsingMojoCdm()) {
DVLOG(0) << "Skipping test; Not working with mojo CDM yet.";
return;
}
TestPlaybackCase(kExternalClearKeyKeySystem, kUnknownSession,
kEmeSessionNotFound);
}
......
......@@ -1921,6 +1921,8 @@ source_set("browser") {
if (enable_library_cdms) {
sources += [
"media/cdm_file_impl.cc",
"media/cdm_file_impl.h",
"media/cdm_storage_impl.cc",
"media/cdm_storage_impl.h",
]
......
This diff is collapsed.
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CONTENT_BROWSER_MEDIA_CDM_FILE_IMPL_H_
#define CONTENT_BROWSER_MEDIA_CDM_FILE_IMPL_H_
#include <string>
#include "base/callback_forward.h"
#include "base/files/file.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "base/threading/thread_checker.h"
#include "media/mojo/interfaces/cdm_storage.mojom.h"
#include "storage/browser/fileapi/async_file_util.h"
#include "url/origin.h"
namespace storage {
class FileSystemContext;
class FileSystemURL;
} // namespace storage
namespace content {
// This class implements the media::mojom::CdmFile interface. It uses the same
// mojo pipe as CdmStorageImpl, to enforce message dispatch order.
class CdmFileImpl final : public media::mojom::CdmFile {
public:
CdmFileImpl(const std::string& file_name,
const url::Origin& origin,
const std::string& file_system_id,
const std::string& file_system_root_uri,
scoped_refptr<storage::FileSystemContext> file_system_context);
~CdmFileImpl() final;
// Called to open the file for read initially. Will create a file with
// |file_name_| if it does not exist. |file_opened_callback| will be called
// with the opened file descriptor on success. |file|.error_details()
// = base::File::FILE_ERROR_IN_USE if the file is in use by other CDMs
// or by the system. Note that |file_opened_callback| may destroy |this|
// (especially if the file can not be opened).
// Note that |this| should not be used anymore if Initialize() fails.
using OpenFileCallback = base::OnceCallback<void(base::File file)>;
void Initialize(OpenFileCallback file_opened_callback);
// media::mojom::CdmFile implementation. |callback| will be called with the
// file descriptor on success. Otherwise the file descriptor will not be
// valid, and error_details() provides the reason.
void OpenFileForWriting(OpenFileForWritingCallback callback) final;
void CommitWrite(CommitWriteCallback callback) final;
private:
using CreateOrOpenCallback = storage::AsyncFileUtil::CreateOrOpenCallback;
// Keep track of which files are locked.
// kFileLocked: Only the original file |file_name_| is locked.
// kFileAndTempFileLocked: Both |file_name_| and |temp_file_name_| are
// locked.
// Initialize() can only be called if kNone, results in kFileLocked (on
// success). OpenFileForWriting() can only be called if kFileLocked, results
// in kFileAndTempFileLocked. CommitWrite() can only be called if
// kFileAndTempFileLocked, results in kFileLocked (temp file closed and then
// renamed to replace the original).
enum class LockState { kNone, kFileLocked, kFileAndTempFileLocked };
// Open the file |file_name| using the flags provided in |file_flags|.
// |callback| is called with the result.
void OpenFile(const std::string& file_name,
uint32_t file_flags,
CreateOrOpenCallback callback);
void OnFileOpenedForReading(base::File file,
const base::Closure& on_close_callback);
void OnTempFileOpenedForWriting(base::File file,
const base::Closure& on_close_callback);
void OnFileRenamed(base::File::Error move_result);
// Returns the FileSystemURL for the specified |file_name|.
storage::FileSystemURL CreateFileSystemURL(const std::string& file_name);
// Helper methods to lock and unlock a file.
bool AcquireFileLock(const std::string& file_name);
bool IsFileLockHeld(const std::string& file_name);
void ReleaseFileLock(const std::string& file_name);
// Names of the files this class represents.
const std::string file_name_;
const std::string temp_file_name_;
// Files are stored in the PluginPrivateFileSystem. The following are needed
// to access files.
const url::Origin origin_;
const std::string file_system_id_;
const std::string file_system_root_uri_;
scoped_refptr<storage::FileSystemContext> file_system_context_;
// Keep track of which files are opened.
LockState lock_state_ = LockState::kNone;
// As only one open operation is allowed at a time, |pending_open_callback_|
// keeps track of the callback to be called when the file is opened. This
// ensures the callback is always called if we are destroyed while the open
// operation is running.
OpenFileCallback pending_open_callback_;
// Callbacks required to close the file when it's no longer needed.
// storage::AsyncFileUtil::CreateOrOpen() returns this callback on a
// successful open along with the base::File object, which should be
// called when the file is closed.
base::Closure on_close_callback_;
base::Closure temporary_file_on_close_callback_;
THREAD_CHECKER(thread_checker_);
base::WeakPtrFactory<CdmFileImpl> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(CdmFileImpl);
};
} // namespace content
#endif // CONTENT_BROWSER_MEDIA_CDM_FILE_IMPL_H_
This diff is collapsed.
......@@ -16,16 +16,14 @@
#include "content/common/content_export.h"
#include "content/public/browser/frame_service_base.h"
#include "media/mojo/interfaces/cdm_storage.mojom.h"
#include "mojo/public/cpp/bindings/strong_associated_binding_set.h"
namespace storage {
class FileSystemContext;
}
namespace url {
class Origin;
}
namespace content {
class CdmFileImpl;
class RenderFrameHost;
// This class implements the media::mojom::CdmStorage using the
......@@ -34,21 +32,6 @@ class RenderFrameHost;
class CONTENT_EXPORT CdmStorageImpl final
: public content::FrameServiceBase<media::mojom::CdmStorage> {
public:
// The file system is different for each CDM and each origin. So track files
// in use based on the tuple CDM file system ID, origin, and file name.
struct FileLockKey {
FileLockKey(const std::string& cdm_file_system_id,
const url::Origin& origin,
const std::string& file_name);
~FileLockKey() = default;
// Allow use as a key in std::set.
bool operator<(const FileLockKey& other) const;
std::string cdm_file_system_id;
url::Origin origin;
std::string file_name;
};
// Check if |cdm_file_system_id| is valid.
static bool IsValidCdmFileSystemId(const std::string& cdm_file_system_id);
......@@ -63,39 +46,54 @@ class CONTENT_EXPORT CdmStorageImpl final
void Open(const std::string& file_name, OpenCallback callback) final;
private:
// File system should only be opened once, so keep track if it has already
// been opened (or is in the process of opening). State is kError if an error
// happens while opening the file system.
enum class FileSystemState { kUnopened, kOpening, kOpened, kError };
CdmStorageImpl(RenderFrameHost* render_frame_host,
const std::string& cdm_file_system_id,
scoped_refptr<storage::FileSystemContext> file_system_context,
media::mojom::CdmStorageRequest request);
~CdmStorageImpl() final;
// Called when the file system has been opened (OpenPluginPrivateFileSystem
// is asynchronous).
void OnFileSystemOpened(const FileLockKey& key,
const std::string& fsid,
OpenCallback callback,
base::File::Error error);
// Called when the file system is opened.
void OnFileSystemOpened(base::File::Error error);
// After the file system is opened, called to create a CdmFile object.
void CreateCdmFile(const std::string& file_name, OpenCallback callback);
// Called when the requested file has been opened.
void OnFileOpened(const FileLockKey& key,
OpenCallback callback,
base::File file,
const base::Closure& on_close_callback);
// Called after the CdmFileImpl object has opened the file for reading.
void OnCdmFileInitialized(std::unique_ptr<CdmFileImpl> cdm_file_impl,
OpenCallback callback,
base::File file);
// Files are stored in the PluginPrivateFileSystem, so keep track of the
// CDM file system ID in order to open the files in the correct context.
const std::string cdm_file_system_id_;
scoped_refptr<storage::FileSystemContext> file_system_context_;
// The PluginPrivateFileSystem only needs to be opened once.
FileSystemState file_system_state_ = FileSystemState::kUnopened;
// As multiple calls to Open() could happen while the file system is being
// opened asynchronously, keep track of the requests so they can be
// processed once the file system is open.
using PendingOpenData = std::pair<std::string, OpenCallback>;
std::vector<PendingOpenData> pending_open_calls_;
// Once the PluginPrivateFileSystem is opened, keep track of the URI that
// refers to it.
std::string file_system_root_uri_;
// This is the child process that will actually read and write the file(s)
// returned, and it needs permission to access the file when it's opened.
// returned, and it needs permission to access the file(s).
const int child_process_id_;
// As a lock is taken on a file when Open() is called, it needs to be
// released if the async operations to open the file haven't completed
// by the time |this| is destroyed. So keep track of FileLockKey for files
// that have a lock on them but failed to finish.
std::set<FileLockKey> pending_open_;
// Keep track of all media::mojom::CdmFile bindings, as each CdmFileImpl
// object keeps a reference to |this|. If |this| goes away unexpectedly,
// all remaining CdmFile bindings will be closed.
mojo::StrongAssociatedBindingSet<media::mojom::CdmFile> cdm_file_bindings_;
base::WeakPtrFactory<CdmStorageImpl> weak_factory_;
......
......@@ -638,6 +638,7 @@ void FileIOTest::OnTestComplete(bool success) {
file_io_stack_.pop();
}
FILE_IO_DVLOG(3) << test_name_ << (success ? " PASSED" : " FAILED");
DLOG_IF(WARNING, !success) << test_name_ << " FAILED";
base::ResetAndReturn(&completion_cb_).Run(success);
}
......
......@@ -125,7 +125,10 @@ source_set("unit_tests") {
}
if (enable_library_cdms) {
sources += [ "services/mojo_cdm_allocator_unittest.cc" ]
sources += [
"services/mojo_cdm_allocator_unittest.cc",
"services/mojo_cdm_file_io_unittest.cc",
]
deps += [ "//media/cdm:cdm_api" ]
}
}
......
......@@ -15,27 +15,42 @@ interface CdmStorage {
kFailure // Unable to open file.
};
// Opens the file specified by |file_name| for read or write. Can be called
// multiple times for different files, or for the same file if the first one
// has been closed. If successful, kSuccess will be returned, |file| can
// be used to access the contents, and |releaser| should be closed when
// access to the file is no longer needed (i.e. file closed). On failure,
// |status| <> kSuccess and |file| and |releaser| are null.
// - If the file is already opened by another CDM instance, kInUse will
// be returned in |status|.
// Opens the file specified by |file_name| for read only. Writing to the file
// is done using a temporary file created with the CdmFile interface returned.
// Can be called multiple times for different files, or for the same file if
// the first one has been closed. If successful, kSuccess will be returned,
// |file_for_reading| can be used to read the contents, and |cdm_file| should
// be closed when access to the file is no longer needed (i.e. file closed).
// On failure, |status| <> kSuccess and |file_for_reading| and |cdm_file|
// are null.
// - If the file is already opened, kInUse will be returned in |status|.
// - |file_name| must not contain forward slash ('/') or backslash ('\'),
// and must not start with an underscore ('_'). If this happens,
// |status| == kFailure is returned.
// - if |key_system| passed to Initialize() is not recognized, kFailure
// will be returned.
Open(string file_name)
=> (Status status,
mojo.common.mojom.File? file,
associated CdmFileReleaser? releaser);
mojo.common.mojom.File? file_for_reading,
associated CdmFile? cdm_file);
};
// Provides a way to keep track of the file opened. When the connection to this
// object is broken, it is assumed that the file has been closed and that no
// more operations will be performed on it.
interface CdmFileReleaser {
//
// This interface is also used to be able to write to the file. Due to sandbox
// restrictions on some platforms (crbug.com/774762), setting the length of the
// file explicitly is not allowed in sandboxed processes. To get around this
// (and avoid file corruption issues), writing is done by opening a new file,
// letting the client write to it, and then replace the original file after
// writing.
interface CdmFile {
// Open a new file that can be written to. This file will be in the same
// directory as the original file. If successful, returns |file_for_writing|
// which can be written to.
OpenFileForWriting() => (mojo.common.mojom.File? file_for_writing);
// Closes the file opened for writing and replaces the original file.
// Returns a new file descriptor that should be used to read the original
// file from now on.
CommitWrite() => (mojo.common.mojom.File? updated_file_for_reading);
};
......@@ -125,6 +125,8 @@ component("services") {
sources += [
"mojo_cdm_allocator.cc",
"mojo_cdm_allocator.h",
"mojo_cdm_file_io.cc",
"mojo_cdm_file_io.h",
"mojo_cdm_helper.cc",
"mojo_cdm_helper.h",
]
......
This diff is collapsed.
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef MEDIA_MOJO_SERVICES_MOJO_CDM_FILE_IO_H_
#define MEDIA_MOJO_SERVICES_MOJO_CDM_FILE_IO_H_
#include <stdint.h>
#include <string>
#include <vector>
#include "base/callback_forward.h"
#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "media/cdm/api/content_decryption_module.h"
#include "media/cdm/cdm_file_io.h"
#include "media/mojo/interfaces/cdm_storage.mojom.h"
#include "media/mojo/services/media_mojo_export.h"
namespace media {
// TODO(crbug.com/774160): This class should do the Read/Write operations on a
// separate thread so as to not impact decoding happening on the same thread.
// The new thread may need to block shutdown so that the file is not corrupted.
// Implements a CdmFileIO that communicates with mojom::CdmStorage.
class MEDIA_MOJO_EXPORT MojoCdmFileIO : public CdmFileIO {
public:
MojoCdmFileIO(cdm::FileIOClient* client, mojom::CdmStorage* cdm_storage);
~MojoCdmFileIO() override;
// CdmFileIO implementation.
void Open(const char* file_name, uint32_t file_name_size) final;
void Read() final;
void Write(const uint8_t* data, uint32_t data_size) final;
void Close() final;
private:
// Allowed state transitions:
// kUnopened -> kOpening -> kOpened
// kUnopened -> kOpening -> kUnopened (if file in use)
// kUnopened -> kOpening -> kError (if file not available)
// kOpened -> kReading -> kOpened
// kOpened -> kWriting -> kOpened
// Once state = kError, only Close() can be called.
enum class State { kUnopened, kOpening, kOpened, kReading, kWriting, kError };
// Error that needs to be reported back to the client.
enum class ErrorType {
kOpenError,
kOpenInUse,
kReadError,
kReadInUse,
kWriteError,
kWriteInUse
};
// Called when the file is opened (or not).
void OnFileOpened(mojom::CdmStorage::Status status,
base::File file,
mojom::CdmFileAssociatedPtrInfo cdm_file);
// Reading the file is done asynchronously.
void DoRead(int64_t num_bytes);
// Called when a temporary file has been opened for writing.
void OnFileOpenedForWriting(const std::vector<uint8_t>& data,
base::File temporary_file);
// Called after the write has been committed and replaces the original file.
void OnWriteCommitted(base::File reopened_file);
// Called when an error occurs. Calls client_->OnXxxxComplete with kError
// or kInUse asynchronously. In some cases we could actually call them
// synchronously, but since these errors shouldn't happen in normal cases,
// we are not optimizing such cases.
void OnError(ErrorType error);
// Callback to notify client of error asynchronously.
void NotifyClientOfError(ErrorType error);
// Results of CdmFileIO operations are sent asynchronously via |client_|.
cdm::FileIOClient* client_;
mojom::CdmStorage* cdm_storage_;
// Keep track of the file being used. As this class can only be used for
// accessing a single file, once |file_name_| is set it shouldn't be changed.
// |file_name_| is only saved for logging purposes.
std::string file_name_;
// Current file open for reading.
base::File file_for_reading_;
// |cdm_file_| is used to write to the file and is released when the file is
// closed so that CdmStorage can tell that the file is no longer being used.
mojom::CdmFileAssociatedPtr cdm_file_;
// Keep track of operations in progress.
State state_ = State::kUnopened;
// NOTE: Weak pointers must be invalidated before all other member variables.
base::WeakPtrFactory<MojoCdmFileIO> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(MojoCdmFileIO);
};
} // namespace media
#endif // MEDIA_MOJO_SERVICES_MOJO_CDM_FILE_IO_H_
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "media/mojo/services/mojo_cdm_file_io.h"
#include "base/files/file.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/run_loop.h"
#include "base/test/scoped_task_environment.h"
#include "media/cdm/api/content_decryption_module.h"
#include "mojo/public/cpp/bindings/strong_associated_binding.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::_;
using ::testing::SaveArg;
using ::testing::StrictMock;
using Status = cdm::FileIOClient::Status;
namespace media {
namespace {
class MockFileIOClient : public cdm::FileIOClient {
public:
MockFileIOClient() {}
~MockFileIOClient() override {}
MOCK_METHOD1(OnOpenComplete, void(Status));
MOCK_METHOD3(OnReadComplete, void(Status, const uint8_t*, uint32_t));
MOCK_METHOD1(OnWriteComplete, void(Status));
};
class MockCdmStorage : public mojom::CdmStorage {
public:
MockCdmStorage() {}
~MockCdmStorage() override {}
bool SetUp() { return temp_directory_.CreateUniqueTempDir(); }
// MojoCdmFileIO calls CdmStorage::Open() to actually open the file.
// Simulate this by creating a file in the temp directory and returning it.
void Open(const std::string& file_name, OpenCallback callback) override {
base::FilePath temp_file = temp_directory_.GetPath().AppendASCII(file_name);
DVLOG(1) << __func__ << " " << temp_file;
base::File file(temp_file, base::File::FLAG_CREATE_ALWAYS |
base::File::FLAG_READ |
base::File::FLAG_WRITE);
std::move(callback).Run(mojom::CdmStorage::Status::kSuccess,
std::move(file), nullptr);
}
private:
base::ScopedTempDir temp_directory_;
};
} // namespace
// Currently MockCdmStorage::Open() returns NULL for the
// CdmFileAssociatedPtrInfo, so it is not possible to connect to a CdmFile
// object when writing data. This will require setting up a mojo connection
// between MojoCdmFileIOTest and CdmStorage, rather than using the object
// directly.
//
// Note that the current browser_test ECKEncryptedMediaTest.FileIOTest
// does test writing (and reading) files using mojo. However, additional
// unittests would be good.
// TODO(crbug.com/777550): Implement tests that write to files.
class MojoCdmFileIOTest : public testing::Test {
protected:
MojoCdmFileIOTest() {}
~MojoCdmFileIOTest() override {}
void SetUp() override {
client_ = std::make_unique<MockFileIOClient>();
cdm_storage_ = std::make_unique<MockCdmStorage>();
ASSERT_TRUE(cdm_storage_->SetUp());
file_io_ =
std::make_unique<MojoCdmFileIO>(client_.get(), cdm_storage_.get());
}
base::test::ScopedTaskEnvironment scoped_task_environment_;
std::unique_ptr<MojoCdmFileIO> file_io_;
std::unique_ptr<MockCdmStorage> cdm_storage_;
std::unique_ptr<MockFileIOClient> client_;
};
TEST_F(MojoCdmFileIOTest, OpenFile) {
const std::string kFileName = "openfile";
EXPECT_CALL(*client_.get(), OnOpenComplete(Status::kSuccess));
file_io_->Open(kFileName.data(), kFileName.length());
base::RunLoop().RunUntilIdle();
}
TEST_F(MojoCdmFileIOTest, OpenFileTwice) {
const std::string kFileName = "openfile";
EXPECT_CALL(*client_.get(), OnOpenComplete(Status::kSuccess));
file_io_->Open(kFileName.data(), kFileName.length());
EXPECT_CALL(*client_.get(), OnOpenComplete(Status::kError));
file_io_->Open(kFileName.data(), kFileName.length());
base::RunLoop().RunUntilIdle();
}
TEST_F(MojoCdmFileIOTest, OpenFileAfterOpen) {
const std::string kFileName = "openfile";
EXPECT_CALL(*client_.get(), OnOpenComplete(Status::kSuccess));
file_io_->Open(kFileName.data(), kFileName.length());
// Run now so that the file is opened.
base::RunLoop().RunUntilIdle();
EXPECT_CALL(*client_.get(), OnOpenComplete(Status::kError));
file_io_->Open(kFileName.data(), kFileName.length());
// Run a second time so Open() tries after the file is already open.
base::RunLoop().RunUntilIdle();
}
TEST_F(MojoCdmFileIOTest, OpenDifferentFiles) {
const std::string kFileName1 = "openfile1";
EXPECT_CALL(*client_.get(), OnOpenComplete(Status::kSuccess));
file_io_->Open(kFileName1.data(), kFileName1.length());
const std::string kFileName2 = "openfile2";
EXPECT_CALL(*client_.get(), OnOpenComplete(Status::kError));
file_io_->Open(kFileName2.data(), kFileName2.length());
base::RunLoop().RunUntilIdle();
}
TEST_F(MojoCdmFileIOTest, OpenBadFileName) {
// Anything other than ASCII letter, digits, and -._ will fail. Add a
// Unicode character to the name.
const std::string kFileName = "openfile\u1234";
EXPECT_CALL(*client_.get(), OnOpenComplete(Status::kError));
file_io_->Open(kFileName.data(), kFileName.length());
base::RunLoop().RunUntilIdle();
}
TEST_F(MojoCdmFileIOTest, OpenTooLongFileName) {
// Limit is 256 characters, so try a file name with 257.
const std::string kFileName(257, 'a');
EXPECT_CALL(*client_.get(), OnOpenComplete(Status::kError));
file_io_->Open(kFileName.data(), kFileName.length());
base::RunLoop().RunUntilIdle();
}
TEST_F(MojoCdmFileIOTest, Read) {
const std::string kFileName = "readfile";
EXPECT_CALL(*client_.get(), OnOpenComplete(Status::kSuccess));
file_io_->Open(kFileName.data(), kFileName.length());
base::RunLoop().RunUntilIdle();
// File doesn't exist, so reading it should return 0 length buffer.
EXPECT_CALL(*client_.get(), OnReadComplete(Status::kSuccess, _, 0));
file_io_->Read();
base::RunLoop().RunUntilIdle();
}
TEST_F(MojoCdmFileIOTest, ReadBeforeOpen) {
// File not open, so reading should fail.
EXPECT_CALL(*client_.get(), OnReadComplete(Status::kError, _, _));
file_io_->Read();
base::RunLoop().RunUntilIdle();
}
TEST_F(MojoCdmFileIOTest, TwoReads) {
const std::string kFileName = "readfile";
EXPECT_CALL(*client_.get(), OnOpenComplete(Status::kSuccess));
file_io_->Open(kFileName.data(), kFileName.length());
base::RunLoop().RunUntilIdle();
EXPECT_CALL(*client_.get(), OnReadComplete(Status::kSuccess, _, 0));
EXPECT_CALL(*client_.get(), OnReadComplete(Status::kInUse, _, 0));
file_io_->Read();
file_io_->Read();
base::RunLoop().RunUntilIdle();
}
} // namespace media
......@@ -7,20 +7,23 @@
#include "media/base/scoped_callback_runner.h"
#include "media/cdm/cdm_helpers.h"
#include "media/mojo/services/mojo_cdm_allocator.h"
#include "media/mojo/services/mojo_cdm_file_io.h"
#include "services/service_manager/public/cpp/connect.h"
namespace media {
MojoCdmHelper::MojoCdmHelper(
service_manager::mojom::InterfaceProvider* interface_provider)
: interface_provider_(interface_provider) {}
: interface_provider_(interface_provider), weak_factory_(this) {}
MojoCdmHelper::~MojoCdmHelper() {}
std::unique_ptr<CdmFileIO> MojoCdmHelper::CreateCdmFileIO(
cdm::FileIOClient* client) {
// TODO(jrummell): Hook up File IO. http://crbug.com/479923.
return nullptr;
ConnectToCdmStorage();
// Pass a reference to CdmStorage so that MojoCdmFileIO can open a file.
return std::make_unique<MojoCdmFileIO>(client, cdm_storage_ptr_.get());
}
cdm::Buffer* MojoCdmHelper::CreateCdmBuffer(size_t capacity) {
......@@ -34,21 +37,17 @@ std::unique_ptr<VideoFrameImpl> MojoCdmHelper::CreateCdmVideoFrame() {
void MojoCdmHelper::QueryStatus(QueryStatusCB callback) {
QueryStatusCB scoped_callback =
ScopedCallbackRunner(std::move(callback), false, 0, 0);
if (!ConnectToOutputProtection())
return;
output_protection_->QueryStatus(std::move(scoped_callback));
ConnectToOutputProtection();
output_protection_ptr_->QueryStatus(std::move(scoped_callback));
}
void MojoCdmHelper::EnableProtection(uint32_t desired_protection_mask,
EnableProtectionCB callback) {
EnableProtectionCB scoped_callback =
ScopedCallbackRunner(std::move(callback), false);
if (!ConnectToOutputProtection())
return;
output_protection_->EnableProtection(desired_protection_mask,
std::move(scoped_callback));
ConnectToOutputProtection();
output_protection_ptr_->EnableProtection(desired_protection_mask,
std::move(scoped_callback));
}
void MojoCdmHelper::ChallengePlatform(const std::string& service_id,
......@@ -56,11 +55,9 @@ void MojoCdmHelper::ChallengePlatform(const std::string& service_id,
ChallengePlatformCB callback) {
ChallengePlatformCB scoped_callback =
ScopedCallbackRunner(std::move(callback), false, "", "", "");
if (!ConnectToPlatformVerification())
return;
platform_verification_->ChallengePlatform(service_id, challenge,
std::move(scoped_callback));
ConnectToPlatformVerification();
platform_verification_ptr_->ChallengePlatform(service_id, challenge,
std::move(scoped_callback));
}
void MojoCdmHelper::GetStorageId(uint32_t version, StorageIdCB callback) {
......@@ -70,28 +67,31 @@ void MojoCdmHelper::GetStorageId(uint32_t version, StorageIdCB callback) {
// http://crbug.com/478960.
}
void MojoCdmHelper::ConnectToCdmStorage() {
if (!cdm_storage_ptr_) {
service_manager::GetInterface<mojom::CdmStorage>(interface_provider_,
&cdm_storage_ptr_);
}
}
CdmAllocator* MojoCdmHelper::GetAllocator() {
if (!allocator_)
allocator_ = base::MakeUnique<MojoCdmAllocator>();
return allocator_.get();
}
bool MojoCdmHelper::ConnectToOutputProtection() {
if (!output_protection_attempted_) {
output_protection_attempted_ = true;
service_manager::GetInterface<mojom::OutputProtection>(interface_provider_,
&output_protection_);
void MojoCdmHelper::ConnectToOutputProtection() {
if (!output_protection_ptr_) {
service_manager::GetInterface<mojom::OutputProtection>(
interface_provider_, &output_protection_ptr_);
}
return output_protection_.is_bound();
}
bool MojoCdmHelper::ConnectToPlatformVerification() {
if (!platform_verification_attempted_) {
platform_verification_attempted_ = true;
void MojoCdmHelper::ConnectToPlatformVerification() {
if (!platform_verification_ptr_) {
service_manager::GetInterface<mojom::PlatformVerification>(
interface_provider_, &platform_verification_);
interface_provider_, &platform_verification_ptr_);
}
return platform_verification_.is_bound();
}
} // namespace media
......@@ -9,7 +9,9 @@
#include "base/compiler_specific.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "media/cdm/cdm_auxiliary_helper.h"
#include "media/mojo/interfaces/cdm_storage.mojom.h"
#include "media/mojo/interfaces/output_protection.mojom.h"
#include "media/mojo/interfaces/platform_verification.mojom.h"
#include "media/mojo/services/media_mojo_export.h"
......@@ -32,8 +34,7 @@ class MEDIA_MOJO_EXPORT MojoCdmHelper final : public CdmAuxiliaryHelper {
~MojoCdmHelper() final;
// CdmAuxiliaryHelper implementation.
std::unique_ptr<media::CdmFileIO> CreateCdmFileIO(
cdm::FileIOClient* client) final;
std::unique_ptr<CdmFileIO> CreateCdmFileIO(cdm::FileIOClient* client) final;
cdm::Buffer* CreateCdmBuffer(size_t capacity) final;
std::unique_ptr<VideoFrameImpl> CreateCdmVideoFrame() final;
void QueryStatus(QueryStatusCB callback) final;
......@@ -46,21 +47,25 @@ class MEDIA_MOJO_EXPORT MojoCdmHelper final : public CdmAuxiliaryHelper {
private:
// All services are created lazily.
void ConnectToCdmStorage();
CdmAllocator* GetAllocator();
bool ConnectToOutputProtection();
bool ConnectToPlatformVerification();
void ConnectToOutputProtection();
void ConnectToPlatformVerification();
// Provides interfaces when needed.
service_manager::mojom::InterfaceProvider* interface_provider_;
// Keep track if connection to the Mojo service has been attempted once.
// The service may not exist, or may fail later.
bool output_protection_attempted_ = false;
bool platform_verification_attempted_ = false;
// Connections to the additional services. For the mojom classes, if a
// connection error occurs, we will not be able to reconnect to the
// service as the document has been destroyed (see FrameServiceBase) or
// the browser crashed, so there's no point in trying to reconnect.
mojom::CdmStoragePtr cdm_storage_ptr_;
std::unique_ptr<CdmAllocator> allocator_;
mojom::OutputProtectionPtr output_protection_ptr_;
mojom::PlatformVerificationPtr platform_verification_ptr_;
std::unique_ptr<media::CdmAllocator> allocator_;
media::mojom::OutputProtectionPtr output_protection_;
media::mojom::PlatformVerificationPtr platform_verification_;
base::WeakPtrFactory<MojoCdmHelper> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(MojoCdmHelper);
};
} // namespace media
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment