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