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 22 namespace 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. 36 class 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