From c3c4c7b9a695ad1dbebe57242ba071103fe9a567 Mon Sep 17 00:00:00 2001 From: John Firebaugh Date: Fri, 5 Feb 2016 17:10:13 -0800 Subject: [core] Interface and implementation for offline --- include/mbgl/storage/default_file_source.hpp | 77 ++++++++++++ include/mbgl/storage/offline.hpp | 181 +++++++++++++++++++++++++++ 2 files changed, 258 insertions(+) create mode 100644 include/mbgl/storage/offline.hpp (limited to 'include/mbgl/storage') diff --git a/include/mbgl/storage/default_file_source.hpp b/include/mbgl/storage/default_file_source.hpp index d8fc4b98a3..e7ffe125b1 100644 --- a/include/mbgl/storage/default_file_source.hpp +++ b/include/mbgl/storage/default_file_source.hpp @@ -2,6 +2,9 @@ #define MBGL_STORAGE_DEFAULT_FILE_SOURCE #include +#include + +#include namespace mbgl { @@ -22,6 +25,80 @@ public: std::unique_ptr request(const Resource&, Callback) override; + /* + * Retrieve all regions in the offline database. + * + * The query will be executed asynchronously and the results passed to the given + * callback, which will be executed on the database thread; it is the responsibility + * of the SDK bindings to re-execute a user-provided callback on the main thread. + */ + void listOfflineRegions(std::function>)>); + + /* + * Create an offline region in the database. + * + * When the initial database queries have completed, the provided callback will be + * executed on the database thread; it is the responsibility of the SDK bindings + * to re-execute a user-provided callback on the main thread. + * + * Note that the resulting region will be in an inactive download state; to begin + * downloading resources, call `setOfflineRegionDownloadState(OfflineRegionDownloadState::Active)`, + * optionally registering an `OfflineRegionObserver` beforehand. + */ + void createOfflineRegion(const OfflineRegionDefinition& definition, + const OfflineRegionMetadata& metadata, + std::function)>); + + /* + * Register an observer to be notified when the state of the region changes. + */ + void setOfflineRegionObserver(OfflineRegion&, std::unique_ptr); + + /* + * Pause or resume downloading of regional resources. + */ + void setOfflineRegionDownloadState(OfflineRegion&, OfflineRegionDownloadState); + + /* + * Retrieve the current status of the region. The query will be executed + * asynchronously and the results passed to the given callback, which will be + * executed on the database thread; it is the responsibility of the SDK bindings + * to re-execute a user-provided callback on the main thread. + */ + void getOfflineRegionStatus(OfflineRegion&, std::function)>) const; + + /* + * Initiate the removal of offline region from the database. + * + * All resources required by the region, but not also required by other regions, will + * become eligible for removal for space-optimization. Because the offline database is + * also used for ambient usage-based caching, and offline resources may still be useful + * for ambient usage, they are not immediately removed. To immediately remove resources + * not used by any extant region, call removeUnusedOfflineResources(). + * + * Note that this method takes ownership of the input, reflecting the fact that once + * region deletion is initiated, it is not legal to perform further actions with the + * region. + * + * When the operation is complete or encounters an error, the given callback will be + * executed on the database thread; it is the responsibility of the SDK bindings + * to re-execute a user-provided callback on the main thread. + */ + void deleteOfflineRegion(OfflineRegion&&, std::function); + + /* + * Remove all resources in the database that are not required by any region, thus + * optimizing the disk space used by the offline database. + * + * When the operation is complete or encounters an error, the given callback will be + * executed on the database thread; it is the responsibility of the SDK bindings + * to re-execute a user-provided callback on the main thread. + */ + void removeUnusedOfflineResources(std::function); + // For testing only. void put(const Resource&, const Response&); void goOffline(); diff --git a/include/mbgl/storage/offline.hpp b/include/mbgl/storage/offline.hpp new file mode 100644 index 0000000000..dd63cf968f --- /dev/null +++ b/include/mbgl/storage/offline.hpp @@ -0,0 +1,181 @@ +#pragma once + +#include +#include +#include +#include + +#include +#include +#include + +namespace mbgl { + +class TileID; +class SourceInfo; + +/* + * An offline region defined by a style URL, geographic bounding box, zoom range, and + * device pixel ratio. + * + * Both minZoom and maxZoom must be ≥ 0, and maxZoom must be ≥ minZoom. + * + * maxZoom may be ∞, in which case for each tile source, the region will include + * tiles from minZoom up to the maximum zoom level provided by that source. + * + * pixelRatio must be ≥ 0 and should typically be 1.0 or 2.0. + */ +class OfflineTilePyramidRegionDefinition { +public: + OfflineTilePyramidRegionDefinition(const std::string&, const LatLngBounds&, double, double, float); + + /* Private */ + std::vector tileCover(SourceType, uint16_t tileSize, const SourceInfo&) const; + + const std::string styleURL; + const LatLngBounds bounds; + const double minZoom; + const double maxZoom; + const float pixelRatio; +}; + +/* + * For the present, a tile pyramid is the only type of offline region. In the future, + * other definition types will be available and this will be a variant type. + */ +using OfflineRegionDefinition = OfflineTilePyramidRegionDefinition; + +/* + * The encoded format is private. + */ +std::string encodeOfflineRegionDefinition(const OfflineRegionDefinition&); +OfflineRegionDefinition decodeOfflineRegionDefinition(const std::string&); + +/* + * Arbitrary binary region metadata. The contents are opaque to the mbgl implementation; + * it just stores and retrieves a BLOB. SDK bindings should leave the interpretation of + * this data up to the application; they _should not_ enforce a higher-level data format. + * In the future we want offline database to be portable across target platforms, and a + * platform-specific metadata format would prevent that. + */ +using OfflineRegionMetadata = std::vector; + +/* + * A region is either inactive (not downloading, but previously-downloaded + * resources are available for use), or active (resources are being downloaded + * or will be downloaded, if necessary, when network access is available). + * + * This state is independent of whether or not the complete set of resources + * is currently available for offline use. To check if that is the case, use + * `OfflineRegionStatus::complete()`. + */ +enum class OfflineRegionDownloadState { + Inactive, + Active +}; + +/* + * A region's status includes its active/inactive state as well as counts + * of the number of resources that have completed downloading, their total + * size in bytes, and the total number of resources that are required. + * + * Note that the total required size in bytes is not currently available. A + * future API release may provide an estimate of this number. + */ +class OfflineRegionStatus { +public: + OfflineRegionDownloadState downloadState = OfflineRegionDownloadState::Inactive; + + /** + * The number of resources that have been fully downloaded and are ready for + * offline access. + */ + uint64_t completedResourceCount = 0; + + /** + * The cumulative size, in bytes, of all resources that have been fully downloaded. + */ + uint64_t completedResourceSize = 0; + + /** + * The number of resources that are known to be required for this region. See the + * documentation for `requiredResourceCountIsIndeterminate` for an important caveat + * about this number. + */ + uint64_t requiredResourceCount = 0; + + /** + * This property is true during early phases of an offline download, when the total + * required resource count is unknown and requiredResourceCount is merely a lower + * bound. + * + * Specifically, it is true before until the style and tile sources have been + * downloaded, and false thereafter. + */ + bool requiredResourceCountIsIndeterminate = true; + + bool complete() const { + return completedResourceCount == requiredResourceCount; + } +}; + +/* + * A region can have a single observer, which gets notified whenever a change + * to the region's status occurs. + */ +class OfflineRegionObserver { +public: + virtual ~OfflineRegionObserver() = default; + + /* + * Implement this method to be notified of a change in the status of an + * offline region. Status changes include any change in state of the members + * of OfflineRegionStatus. + * + * Note that this method will be executed on the database thread; it is the + * responsibility of the SDK bindings to wrap this object in an interface that + * re-executes the user-provided implementation on the main thread. + */ + virtual void statusChanged(OfflineRegionStatus) {} + + /* + * Implement this method to be notified of errors encountered while downloading + * regional resources. Such errors may be recoverable; for example the implementation + * will attempt to re-request failed resources based on an exponential backoff + * algorithm, or when it detects that network access has been restored. + * + * Note that this method will be executed on the database thread; it is the + * responsibility of the SDK bindings to wrap this object in an interface that + * re-executes the user-provided implementation on the main thread. + */ + virtual void responseError(Response::Error) {} +}; + +class OfflineRegion { +public: + // Move-only; not publicly constructible. + OfflineRegion(OfflineRegion&&); + OfflineRegion& operator=(OfflineRegion&&); + ~OfflineRegion(); + + OfflineRegion() = delete; + OfflineRegion(const OfflineRegion&) = delete; + OfflineRegion& operator=(const OfflineRegion&) = delete; + + int64_t getID() const; + const OfflineRegionDefinition& getDefinition() const; + const OfflineRegionMetadata& getMetadata() const; + +private: + friend class OfflineDatabase; + + OfflineRegion(int64_t id, + const OfflineRegionDefinition&, + const OfflineRegionMetadata&); + + const int64_t id; + const OfflineRegionDefinition definition; + const OfflineRegionMetadata metadata; +}; + +} // namespace mbgl -- cgit v1.2.1