1/* 2 * Copyright 2010, The Android Open Source Project 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * * Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * * Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26#include "config.h" 27#include "GeolocationPositionCache.h" 28 29#include "Geoposition.h" 30#include "SQLValue.h" 31#include "SQLiteDatabase.h" 32#include "SQLiteFileSystem.h" 33#include "SQLiteStatement.h" 34#include "SQLiteTransaction.h" 35 36 37namespace WebCore { 38 39static const char* databaseName = "CachedGeoposition.db"; 40 41int GeolocationPositionCache::s_instances = 0; 42RefPtr<Geoposition>* GeolocationPositionCache::s_cachedPosition; 43String* GeolocationPositionCache::s_databaseFile = 0; 44 45GeolocationPositionCache::GeolocationPositionCache() 46{ 47 if (!(s_instances++)) { 48 s_cachedPosition = new RefPtr<Geoposition>; 49 *s_cachedPosition = readFromDB(); 50 } 51} 52 53GeolocationPositionCache::~GeolocationPositionCache() 54{ 55 if (!(--s_instances)) { 56 if (*s_cachedPosition) 57 writeToDB(s_cachedPosition->get()); 58 delete s_cachedPosition; 59 } 60} 61 62void GeolocationPositionCache::setCachedPosition(Geoposition* cachedPosition) 63{ 64 *s_cachedPosition = cachedPosition; 65} 66 67Geoposition* GeolocationPositionCache::cachedPosition() 68{ 69 return s_cachedPosition->get(); 70} 71 72void GeolocationPositionCache::setDatabasePath(const String& databasePath) 73{ 74 if (!s_databaseFile) 75 s_databaseFile = new String; 76 *s_databaseFile = SQLiteFileSystem::appendDatabaseFileNameToPath(databasePath, databaseName); 77 // If we don't have have a cached position, attempt to read one from the 78 // DB at the new path. 79 if (s_instances && !(*s_cachedPosition)) 80 *s_cachedPosition = readFromDB(); 81} 82 83PassRefPtr<Geoposition> GeolocationPositionCache::readFromDB() 84{ 85 SQLiteDatabase database; 86 if (!s_databaseFile || !database.open(*s_databaseFile)) 87 return 0; 88 89 // Create the table here, such that even if we've just created the 90 // DB, the commands below should succeed. 91 if (!database.executeCommand("CREATE TABLE IF NOT EXISTS CachedPosition (" 92 "latitude REAL NOT NULL, " 93 "longitude REAL NOT NULL, " 94 "altitude REAL, " 95 "accuracy REAL NOT NULL, " 96 "altitudeAccuracy REAL, " 97 "heading REAL, " 98 "speed REAL, " 99 "timestamp INTEGER NOT NULL)")) 100 return 0; 101 102 SQLiteStatement statement(database, "SELECT * FROM CachedPosition"); 103 if (statement.prepare() != SQLResultOk) 104 return 0; 105 106 if (statement.step() != SQLResultRow) 107 return 0; 108 109 bool providesAltitude = statement.getColumnValue(2).type() != SQLValue::NullValue; 110 bool providesAltitudeAccuracy = statement.getColumnValue(4).type() != SQLValue::NullValue; 111 bool providesHeading = statement.getColumnValue(5).type() != SQLValue::NullValue; 112 bool providesSpeed = statement.getColumnValue(6).type() != SQLValue::NullValue; 113 RefPtr<Coordinates> coordinates = Coordinates::create(statement.getColumnDouble(0), // latitude 114 statement.getColumnDouble(1), // longitude 115 providesAltitude, statement.getColumnDouble(2), // altitude 116 statement.getColumnDouble(3), // accuracy 117 providesAltitudeAccuracy, statement.getColumnDouble(4), // altitudeAccuracy 118 providesHeading, statement.getColumnDouble(5), // heading 119 providesSpeed, statement.getColumnDouble(6)); // speed 120 return Geoposition::create(coordinates.release(), statement.getColumnInt64(7)); // timestamp 121} 122 123void GeolocationPositionCache::writeToDB(const Geoposition* position) 124{ 125 ASSERT(position); 126 127 SQLiteDatabase database; 128 if (!s_databaseFile || !database.open(*s_databaseFile)) 129 return; 130 131 SQLiteTransaction transaction(database); 132 133 if (!database.executeCommand("DELETE FROM CachedPosition")) 134 return; 135 136 SQLiteStatement statement(database, "INSERT INTO CachedPosition (" 137 "latitude, " 138 "longitude, " 139 "altitude, " 140 "accuracy, " 141 "altitudeAccuracy, " 142 "heading, " 143 "speed, " 144 "timestamp) " 145 "VALUES (?, ?, ?, ?, ?, ?, ?, ?)"); 146 if (statement.prepare() != SQLResultOk) 147 return; 148 149 statement.bindDouble(1, position->coords()->latitude()); 150 statement.bindDouble(2, position->coords()->longitude()); 151 if (position->coords()->canProvideAltitude()) 152 statement.bindDouble(3, position->coords()->altitude()); 153 else 154 statement.bindNull(3); 155 statement.bindDouble(4, position->coords()->accuracy()); 156 if (position->coords()->canProvideAltitudeAccuracy()) 157 statement.bindDouble(5, position->coords()->altitudeAccuracy()); 158 else 159 statement.bindNull(5); 160 if (position->coords()->canProvideHeading()) 161 statement.bindDouble(6, position->coords()->heading()); 162 else 163 statement.bindNull(6); 164 if (position->coords()->canProvideSpeed()) 165 statement.bindDouble(7, position->coords()->speed()); 166 else 167 statement.bindNull(7); 168 statement.bindInt64(8, position->timestamp()); 169 if (!statement.executeCommand()) 170 return; 171 172 transaction.commit(); 173} 174 175} // namespace WebCore 176