Bug 941357 - (Part 1) Consolidate per-profile content provider logic in PerProfileDatabases, starting with TabsProvider. r=rnewman
authorMargaret Leibovic <margaret.leibovic@gmail.com>
Mon, 09 Dec 2013 09:05:59 -0800
changeset 159434 f120be7494106b413fd51f1d177aaa21d6e0bce3
parent 159433 a3d8e8527e941cee9d8679079dbb9c0a438a712d
child 159435 904545658c9bb78b9dc4fff9ef44d7fcee9dd8e6
push id3870
push usermleibovic@mozilla.com
push dateMon, 09 Dec 2013 17:06:14 +0000
treeherderfx-team@904545658c9b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrnewman
bugs941357
milestone28.0a1
Bug 941357 - (Part 1) Consolidate per-profile content provider logic in PerProfileDatabases, starting with TabsProvider. r=rnewman
mobile/android/base/db/PerProfileDatabases.java
mobile/android/base/db/TabsProvider.java
mobile/android/base/moz.build
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/db/PerProfileDatabases.java
@@ -0,0 +1,69 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.gecko.db;
+
+import java.io.File;
+import java.util.HashMap;
+
+import org.mozilla.gecko.GeckoProfile;
+
+import android.content.Context;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.text.TextUtils;
+
+/**
+ * Manages a set of per-profile database storage helpers.
+ */
+public class PerProfileDatabases<T extends SQLiteOpenHelper> {
+
+    private final HashMap<String, T> mStorages = new HashMap<String, T>();
+
+    private final Context mContext;
+    private final String mDatabaseName;
+    private final DatabaseHelperFactory<T> mHelperFactory;
+
+    public interface DatabaseHelperFactory<T> {
+        public T makeDatabaseHelper(Context context, String databasePath);
+    }
+
+    public PerProfileDatabases(final Context context, final String databaseName, final DatabaseHelperFactory<T> helperFactory) {
+        mContext = context;
+        mDatabaseName = databaseName;
+        mHelperFactory = helperFactory;
+    }
+
+    public String getDatabasePathForProfile(String profile) {
+        final File profileDir = GeckoProfile.get(mContext, profile).getDir();
+        if (profileDir == null) {
+            return null;
+        }
+
+        return new File(profileDir, mDatabaseName).getAbsolutePath();
+    }
+
+    public T getDatabaseHelperForProfile(String profile) {
+        // Always fall back to default profile if none has been provided.
+        if (TextUtils.isEmpty(profile)) {
+            profile = GeckoProfile.get(mContext).getName();
+        }
+
+        synchronized (this) {
+            if (mStorages.containsKey(profile)) {
+                return mStorages.get(profile);
+            }
+
+            final String databasePath = getDatabasePathForProfile(profile);
+            if (databasePath == null) {
+                throw new IllegalStateException("Database path is null for profile: " + profile);
+            }
+
+            final T helper = mHelperFactory.makeDatabaseHelper(mContext, databasePath);
+            DBUtils.ensureDatabaseIsNotLocked(helper, databasePath);
+
+            mStorages.put(profile, helper);
+            return helper;
+        }
+    }
+}
--- a/mobile/android/base/db/TabsProvider.java
+++ b/mobile/android/base/db/TabsProvider.java
@@ -7,16 +7,17 @@ package org.mozilla.gecko.db;
 import java.io.File;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
 
 import org.mozilla.gecko.GeckoProfile;
 import org.mozilla.gecko.db.BrowserContract.Clients;
 import org.mozilla.gecko.db.BrowserContract.Tabs;
+import org.mozilla.gecko.db.PerProfileDatabases.DatabaseHelperFactory;
 import org.mozilla.gecko.mozglue.RobocopTarget;
 
 import android.content.ContentProvider;
 import android.content.ContentUris;
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.UriMatcher;
 import android.database.Cursor;
@@ -28,16 +29,18 @@ import android.net.Uri;
 import android.os.Build;
 import android.text.TextUtils;
 import android.util.Log;
 
 public class TabsProvider extends ContentProvider {
     private static final String LOGTAG = "GeckoTabsProvider";
     private Context mContext;
 
+    private PerProfileDatabases<TabsDatabaseHelper> mDatabases;
+
     static final String DATABASE_NAME = "tabs.db";
 
     static final int DATABASE_VERSION = 2;
 
     static final String TABLE_TABS = "tabs";
     static final String TABLE_CLIENTS = "clients";
 
     static final int TABS = 600;
@@ -80,18 +83,16 @@ public class TabsProvider extends Conten
 
         map = new HashMap<String, String>();
         map.put(Clients.GUID, Clients.GUID);
         map.put(Clients.NAME, Clients.NAME);
         map.put(Clients.LAST_MODIFIED, Clients.LAST_MODIFIED);
         CLIENTS_PROJECTION_MAP = Collections.unmodifiableMap(map);
     }
 
-    private HashMap<String, DatabaseHelper> mDatabasePerProfile;
-
     static final String selectColumn(String table, String column) {
         return table + "." + column + " = ?";
     }
 
     // Calculate these once, at initialization. isLoggable is too expensive to
     // have in-line in each log call.
     private static boolean logDebug   = Log.isLoggable(LOGTAG, Log.DEBUG);
     private static boolean logVerbose = Log.isLoggable(LOGTAG, Log.VERBOSE);
@@ -102,18 +103,18 @@ public class TabsProvider extends Conten
     }
 
     protected static void debug(String message) {
         if (logDebug) {
             Log.d(LOGTAG, message);
         }
     }
 
-    final class DatabaseHelper extends SQLiteOpenHelper {
-        public DatabaseHelper(Context context, String databasePath) {
+    final class TabsDatabaseHelper extends SQLiteOpenHelper {
+        public TabsDatabaseHelper(Context context, String databasePath) {
             super(context, databasePath, null, DATABASE_VERSION);
         }
 
         @Override
         public void onCreate(SQLiteDatabase db) {
             debug("Creating tabs.db: " + db.getPath());
             debug("Creating " + TABLE_TABS + " table");
 
@@ -202,100 +203,56 @@ public class TabsProvider extends Conten
                 } finally {
                     if (cursor != null)
                         cursor.close();
                 }
             }
         }
     }
 
-    private DatabaseHelper getDatabaseHelperForProfile(String profile) {
-        // Each profile has a separate tabs.db database. The target
-        // profile is provided using a URI query argument in each request
-        // to our content provider.
-
-        // Always fallback to default profile if none has been provided.
-        if (TextUtils.isEmpty(profile)) {
-            profile = GeckoProfile.get(getContext()).getName();
-        }
-
-        DatabaseHelper dbHelper;
-        synchronized (this) {
-            dbHelper = mDatabasePerProfile.get(profile);
-            if (dbHelper != null) {
-                return dbHelper;
-            }
-
-            String databasePath = getDatabasePath(profile);
-
-            // Before bug 768532, the database was located outside if the
-            // profile on Android 2.2. Make sure it is moved inside the profile
-            // directory.
-            if (Build.VERSION.SDK_INT == 8) {
-                File oldPath = mContext.getDatabasePath("tabs-" + profile + ".db");
-                if (oldPath.exists()) {
-                    oldPath.renameTo(new File(databasePath));
-                }
-            }
-
-            dbHelper = new DatabaseHelper(getContext(), databasePath);
-            mDatabasePerProfile.put(profile, dbHelper);
-
-            DBUtils.ensureDatabaseIsNotLocked(dbHelper, databasePath);
-        }
-
-        debug("Created database helper for profile: " + profile);
-        return dbHelper;
-    }
-
     @RobocopTarget
     private String getDatabasePath(String profile) {
-        trace("Getting database path for profile: " + profile);
-
-        File profileDir = GeckoProfile.get(mContext, profile).getDir();
-        if (profileDir == null) {
-            debug("Couldn't find directory for profile: " + profile);
-            return null;
-        }
-
-        String databasePath = new File(profileDir, DATABASE_NAME).getAbsolutePath();
-        debug("Successfully created database path for profile: " + databasePath);
-
-        return databasePath;
+        return mDatabases.getDatabasePathForProfile(profile);
     }
 
     private SQLiteDatabase getReadableDatabase(Uri uri) {
         trace("Getting readable database for URI: " + uri);
 
         String profile = null;
 
         if (uri != null)
             profile = uri.getQueryParameter(BrowserContract.PARAM_PROFILE);
 
-        return getDatabaseHelperForProfile(profile).getReadableDatabase();
+        return mDatabases.getDatabaseHelperForProfile(profile).getReadableDatabase();
     }
 
     private SQLiteDatabase getWritableDatabase(Uri uri) {
         trace("Getting writable database for URI: " + uri);
 
         String profile = null;
 
         if (uri != null)
             profile = uri.getQueryParameter(BrowserContract.PARAM_PROFILE);
 
-        return getDatabaseHelperForProfile(profile).getWritableDatabase();
+        return mDatabases.getDatabaseHelperForProfile(profile).getWritableDatabase();
     }
 
     @Override
     public boolean onCreate() {
         debug("Creating TabsProvider");
 
         synchronized (this) {
             mContext = getContext();
-            mDatabasePerProfile = new HashMap<String, DatabaseHelper>();
+            mDatabases = new PerProfileDatabases<TabsDatabaseHelper>(
+                getContext(), DATABASE_NAME, new DatabaseHelperFactory<TabsDatabaseHelper>() {
+                    @Override
+                    public TabsDatabaseHelper makeDatabaseHelper(Context context, String databasePath) {
+                        return new TabsDatabaseHelper(context, databasePath);
+                    }
+                });
         }
 
         return true;
     }
 
     @Override
     public String getType(Uri uri) {
         final int match = URI_MATCHER.match(uri);
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -112,16 +112,17 @@ gbjar.sources += [
     'db/BrowserContract.java',
     'db/BrowserDB.java',
     'db/BrowserProvider.java',
     'db/DBUtils.java',
     'db/FormHistoryProvider.java',
     'db/LocalBrowserDB.java',
     'db/PasswordsProvider.java',
     'db/PerProfileContentProvider.java',
+    'db/PerProfileDatabases.java',
     'db/TabsProvider.java',
     'Distribution.java',
     'DoorHanger.java',
     'DoorHangerPopup.java',
     'EditBookmarkDialog.java',
     'favicons/cache/FaviconCache.java',
     'favicons/cache/FaviconCacheElement.java',
     'favicons/cache/FaviconsForURL.java',