Home | History | Annotate | Download | only in ppapi
      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