1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
|
// Copyright 2013 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_CDM_PPAPI_CDM_FILE_IO_IMPL_H_
#define MEDIA_CDM_PPAPI_CDM_FILE_IO_IMPL_H_
#include <algorithm>
#include <string>
#include <vector>
#include "base/basictypes.h"
#include "media/cdm/ppapi/api/content_decryption_module.h"
#include "ppapi/c/ppb_file_io.h"
#include "ppapi/cpp/file_io.h"
#include "ppapi/cpp/file_ref.h"
#include "ppapi/cpp/instance.h"
#include "ppapi/cpp/module.h"
#include "ppapi/cpp/private/isolated_file_system_private.h"
#include "ppapi/utility/completion_callback_factory.h"
namespace media {
// Due to PPAPI limitations, all functions must be called on the main thread.
//
// Implementation notes about states:
// 1, When a method is called in an invalid state (e.g. Read() before Open() is
// called, Write() before Open() finishes or Open() after Open()), kError
// will be returned. The state of |this| will not change.
// 2, When the file is opened by another CDM instance, or when we call Read()/
// Write() during a pending Read()/Write(), kInUse will be returned. The
// state of |this| will not change.
// 3, When a pepper operation failed (either synchronously or asynchronously),
// kError will be returned. The state of |this| will be set to ERROR.
// 4. Any operation in ERROR state will end up with kError.
class CdmFileIOImpl : public cdm::FileIO {
public:
// A class that helps release |file_lock_map_|.
// There should be only one instance of ResourceTracker in a process. Also,
// ResourceTracker should outlive all CdmFileIOImpl instances.
class ResourceTracker {
public:
ResourceTracker();
~ResourceTracker();
private:
DISALLOW_COPY_AND_ASSIGN(ResourceTracker);
};
// After the first successful file read, call |first_file_read_cb| to report
// the file size. |first_file_read_cb| takes one parameter: the file size in
// bytes.
CdmFileIOImpl(cdm::FileIOClient* client,
PP_Instance pp_instance,
const pp::CompletionCallback& first_file_read_cb);
// cdm::FileIO implementation.
virtual void Open(const char* file_name, uint32_t file_name_size) override;
virtual void Read() override;
virtual void Write(const uint8_t* data, uint32_t data_size) override;
virtual void Close() override;
private:
// TODO(xhwang): Introduce more detailed states for UMA logging if needed.
enum State {
STATE_UNOPENED,
STATE_OPENING_FILE_SYSTEM,
STATE_FILE_SYSTEM_OPENED,
STATE_READING,
STATE_WRITING,
STATE_CLOSED,
STATE_ERROR
};
enum ErrorType {
OPEN_WHILE_IN_USE,
READ_WHILE_IN_USE,
WRITE_WHILE_IN_USE,
OPEN_ERROR,
READ_ERROR,
WRITE_ERROR
};
// Always use Close() to release |this| object.
virtual ~CdmFileIOImpl();
// |file_id_| -> |is_file_lock_acquired_| map.
// Design detail:
// - We never erase an entry from this map.
// - Pros: When the same file is read or written repeatedly, we don't need to
// insert/erase the entry repeatedly, which is expensive.
// - Cons: If there are a lot of one-off files used, this map will be
// unnecessarily large. But this should be a rare case.
// - Ideally we could use unordered_map for this. But unordered_set is only
// available in C++11.
typedef std::map<std::string, bool> FileLockMap;
// File lock map shared by all CdmFileIOImpl objects to prevent read/write
// race. A CdmFileIOImpl object tries to acquire a lock before opening a
// file. If the file open failed, the lock is released. Otherwise, the
// CdmFileIOImpl object holds the lock until Close() is called.
// TODO(xhwang): Investigate the following cases and make sure we are good:
// - This assumes all CDM instances run in the same process for a given file
// system.
// - When multiple CDM instances are running in different profiles (e.g.
// normal/incognito window, multiple profiles), we may be overlocking.
static FileLockMap* file_lock_map_;
// Sets |file_id_|. Returns false if |file_id_| cannot be set (e.g. origin URL
// cannot be fetched).
bool SetFileID();
// Acquires the file lock. Returns true if the lock is successfully acquired.
// After the lock is acquired, other cdm::FileIO objects in the same process
// and in the same origin will get kInUse when trying to open the same file.
bool AcquireFileLock();
// Releases the file lock so that the file can be opened by other cdm::FileIO
// objects.
void ReleaseFileLock();
// Helper functions for Open().
void OpenFileSystem();
void OnFileSystemOpened(int32_t result, pp::FileSystem file_system);
// Helper functions for Read().
void OpenFileForRead();
void OnFileOpenedForRead(int32_t result);
void ReadFile();
void OnFileRead(int32_t bytes_read);
// Helper functions for Write(). We always write data to a temporary file,
// then rename the temporary file to the target file. This can prevent data
// corruption if |this| is Close()'ed while waiting for writing to complete.
// However, if Close() is called after OpenTempFileForWrite() but before
// RenameTempFile(), we may still end up with an empty, partially written or
// fully written temporary file in the file system. This temporary file will
// be truncated next time OpenTempFileForWrite() is called.
void OpenTempFileForWrite();
void OnTempFileOpenedForWrite(int32_t result);
void WriteTempFile();
void OnTempFileWritten(int32_t bytes_written);
// Note: pp::FileRef::Rename() actually does a "move": if the target file
// exists, Rename() will succeed and the target file will be overwritten.
// See PepperInternalFileRefBackend::Rename() for implementation detail.
void RenameTempFile();
void OnTempFileRenamed(int32_t result);
// Reset |this| to a clean state.
void Reset();
// For real open/read/write errors, Reset() and set the |state_| to ERROR.
// 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_type);
// Callback to notify client of error asynchronously.
void NotifyClientOfError(int32_t result, ErrorType error_type);
State state_;
// Non-owning pointer.
cdm::FileIOClient* const client_;
const pp::InstanceHandle pp_instance_handle_;
// Format: /<requested_file_name>
std::string file_name_;
// A string ID that uniquely identifies a file in the user's profile.
// It consists of the origin of the document URL (including scheme, host and
// port, delimited by colons) and the |file_name_|.
// For example: http:example.com:8080/foo_file.txt
std::string file_id_;
pp::IsolatedFileSystemPrivate isolated_file_system_;
pp::FileSystem file_system_;
// Shared between read and write. During read, |file_ref_| refers to the real
// file to read data from. During write, it refers to the temporary file to
// write data into.
pp::FileIO file_io_;
pp::FileRef file_ref_;
// A temporary buffer to hold (partial) data to write or the data that has
// been read. The size of |io_buffer_| is always "bytes to write" or "bytes to
// read". Use "char" instead of "unit8_t" because PPB_FileIO uses char* for
// binary data read and write.
std::vector<char> io_buffer_;
// Offset into the file for reading/writing data. When writing data to the
// file, this is also the offset to the |io_buffer_|.
size_t io_offset_;
// Buffer to hold all read data requested. This buffer is passed to |client_|
// when read completes.
std::vector<char> cumulative_read_buffer_;
bool first_file_read_reported_;
// Callback to report the file size in bytes after the first successful read.
pp::CompletionCallback first_file_read_cb_;
pp::CompletionCallbackFactory<CdmFileIOImpl> callback_factory_;
DISALLOW_COPY_AND_ASSIGN(CdmFileIOImpl);
};
} // namespace media
#endif // MEDIA_CDM_PPAPI_CDM_FILE_IO_IMPL_H_
|