Store Geolocation cached position between browser sessions.

This fixes bug http://b/issue?id=2054431.

Change-Id: I44b752b0c7fb4529027018c99dd945279b594b89
diff --git a/WebCore/page/Geolocation.cpp b/WebCore/page/Geolocation.cpp
index adf5eb1..d7e0cc3 100644
--- a/WebCore/page/Geolocation.cpp
+++ b/WebCore/page/Geolocation.cpp
@@ -32,6 +32,10 @@
 #include "Document.h"
 #include "Frame.h"
 #include "Page.h"
+#include "SQLiteDatabase.h"
+#include "SQLiteStatement.h"
+#include "SQLiteTransaction.h"
+#include "SQLValue.h"
 
 namespace WebCore {
 
@@ -96,17 +100,22 @@
     m_geolocation->requestTimedOut(this);
 }
 
+static const char* databaseName = "/CachedPosition.db";
+
 class CachedPositionManager {
   public:
     CachedPositionManager()
     {
-        if (s_instances++ == 0)
+        if (s_instances++ == 0) {
             s_cachedPosition = new RefPtr<Geoposition>;
+            *s_cachedPosition = readFromDB();
+        }
     }
     ~CachedPositionManager()
     {
         if (--s_instances == 0) {
-            // TODO(steveblock): Store cached position between sessions.
+            if (*s_cachedPosition)
+                writeToDB(s_cachedPosition->get());
             delete s_cachedPosition;
         }
     }
@@ -119,14 +128,114 @@
     {
         return s_cachedPosition->get();
     }
+    static void setDatabasePath(String databasePath)
+    {
+        s_databaseFile = databasePath + databaseName;
+        // If we don't have have a cached position, attempt to read one from the
+        // DB at the new path.
+        if (s_instances && *s_cachedPosition == 0)
+            *s_cachedPosition = readFromDB();
+    }
 
- private:
+  private:
+    static PassRefPtr<Geoposition> readFromDB()
+    {
+        SQLiteDatabase database;
+        if (!database.open(s_databaseFile))
+            return 0;
+
+        // Create the table here, such that even if we've just created the
+        // DB, the commands below should succeed.
+        if (!database.executeCommand("CREATE TABLE IF NOT EXISTS CachedPosition ("
+                "latitude REAL NOT NULL, "
+                "longitude REAL NOT NULL, "
+                "altitude REAL, "
+                "accuracy REAL NOT NULL, "
+                "altitudeAccuracy REAL, "
+                "heading REAL, "
+                "speed REAL, "
+                "timestamp INTEGER NOT NULL)"))
+            return 0;
+
+        SQLiteStatement statement(database, "SELECT * FROM CachedPosition");
+        if (statement.prepare() != SQLResultOk)
+            return 0;
+
+        if (statement.step() != SQLResultRow)
+            return 0;
+
+        bool providesAltitude = statement.getColumnValue(2).type() != SQLValue::NullValue;
+        bool providesAltitudeAccuracy = statement.getColumnValue(4).type() != SQLValue::NullValue;
+        bool providesHeading = statement.getColumnValue(5).type() != SQLValue::NullValue;
+        bool providesSpeed = statement.getColumnValue(6).type() != SQLValue::NullValue;
+        RefPtr<Coordinates> coordinates = Coordinates::create(statement.getColumnDouble(0),  // latitude
+                                                              statement.getColumnDouble(1),  // longitude
+                                                              providesAltitude, statement.getColumnDouble(2), // altitude
+                                                              statement.getColumnDouble(3),  // accuracy
+                                                              providesAltitudeAccuracy, statement.getColumnDouble(4), // altitudeAccuracy
+                                                              providesHeading, statement.getColumnDouble(5), // heading
+                                                              providesSpeed, statement.getColumnDouble(6)); // speed
+        return Geoposition::create(coordinates.release(), statement.getColumnInt64(7));  // timestamp
+    }
+    static void writeToDB(Geoposition* position)
+    {
+        ASSERT(position);
+
+        SQLiteDatabase database;
+        if (!database.open(s_databaseFile))
+            return;
+
+        SQLiteTransaction transaction(database);
+
+        if (!database.executeCommand("DELETE FROM CachedPosition"))
+            return;
+
+        SQLiteStatement statement(database, "INSERT INTO CachedPosition ("
+            "latitude, "
+            "longitude, "
+            "altitude, "
+            "accuracy, "
+            "altitudeAccuracy, "
+            "heading, "
+            "speed, "
+            "timestamp) "
+            "VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
+        if (statement.prepare() != SQLResultOk)
+            return;
+
+        statement.bindDouble(1, position->coords()->latitude());
+        statement.bindDouble(2, position->coords()->longitude());
+        if (position->coords()->canProvideAltitude())
+            statement.bindDouble(3, position->coords()->altitude());
+        else
+            statement.bindNull(3);
+        statement.bindDouble(4, position->coords()->accuracy());
+        if (position->coords()->canProvideAltitudeAccuracy())
+            statement.bindDouble(5, position->coords()->altitudeAccuracy());
+        else
+            statement.bindNull(5);
+        if (position->coords()->canProvideHeading())
+            statement.bindDouble(6, position->coords()->heading());
+        else
+            statement.bindNull(6);
+        if (position->coords()->canProvideSpeed())
+            statement.bindDouble(7, position->coords()->speed());
+        else
+            statement.bindNull(7);
+        statement.bindInt64(8, position->timestamp());
+        if (!statement.executeCommand())
+            return;
+
+        transaction.commit();
+    }
     static int s_instances;
     static RefPtr<Geoposition>* s_cachedPosition;
+    static String s_databaseFile;
 };
 
 int CachedPositionManager::s_instances = 0;
 RefPtr<Geoposition>* CachedPositionManager::s_cachedPosition;
+String CachedPositionManager::s_databaseFile;
 
 
 Geolocation::Geolocation(Frame* frame)
@@ -443,4 +552,9 @@
     handleError(service->lastError());
 }
 
+void Geolocation::setDatabasePath(String databasePath)
+{
+    CachedPositionManager::setDatabasePath(databasePath);
+}
+
 } // namespace WebCore
diff --git a/WebCore/page/Geolocation.h b/WebCore/page/Geolocation.h
index 0a19ec2..c97c3e6 100644
--- a/WebCore/page/Geolocation.h
+++ b/WebCore/page/Geolocation.h
@@ -72,6 +72,8 @@
     void setShouldClearCache(bool shouldClearCache) { m_shouldClearCache = shouldClearCache; }
     bool shouldClearCache() const { return m_shouldClearCache; }
 
+    static void setDatabasePath(String);
+
 private:
     Geolocation(Frame*);
 
diff --git a/WebKit/android/jni/WebSettings.cpp b/WebKit/android/jni/WebSettings.cpp
index a94f6df..3cba99c 100644
--- a/WebKit/android/jni/WebSettings.cpp
+++ b/WebKit/android/jni/WebSettings.cpp
@@ -35,6 +35,7 @@
 #include "Frame.h"
 #include "FrameLoader.h"
 #include "FrameView.h"
+#include "Geolocation.h"
 #include "GeolocationPermissions.h"
 #include "Page.h"
 #include "RenderTable.h"
@@ -358,8 +359,10 @@
         flag = env->GetBooleanField(obj, gFieldIds->mGeolocationEnabled);
         GeolocationPermissions::setAlwaysDeny(!flag);
         str = (jstring)env->GetObjectField(obj, gFieldIds->mGeolocationDatabasePath);
-        if (str)
+        if (str) {
             GeolocationPermissions::setDatabasePath(to_string(env,str));
+            WebCore::Geolocation::setDatabasePath(to_string(env,str));
+        }
     }
 };