Bug 746860 - Ensure profiles are moved to internal storage before accessing them. Use migrator. r=blassey a=mfinkle
authorGian-Carlo Pascutto <gpascutto@mozilla.com>
Tue, 15 May 2012 15:13:28 +0200
changeset 95759 fe0ca32428931fd3ccd95e05f23ea91971addcce
parent 95758 14759622c2e5f5812dc1e565b9952615d590b61e
child 95760 c6e3fafe4fb7544dd50e19335ba7cb2cfe89bfea
push id886
push userlsblakk@mozilla.com
push dateMon, 04 Jun 2012 19:57:52 +0000
treeherdermozilla-beta@bbd8d5efd6d1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersblassey, mfinkle
bugs746860
milestone14.0a2
Bug 746860 - Ensure profiles are moved to internal storage before accessing them. Use migrator. r=blassey a=mfinkle
mobile/android/base/GeckoApp.java
mobile/android/base/GeckoAppShell.java
mobile/android/base/GeckoProfile.java
mobile/android/base/ProfileMigrator.java
mobile/android/base/db/BrowserProvider.java.in
--- a/mobile/android/base/GeckoApp.java
+++ b/mobile/android/base/GeckoApp.java
@@ -2237,18 +2237,17 @@ abstract public class GeckoApp
 
         if (profileDir != null) {
             final GeckoApp app = GeckoApp.mAppContext;
 
             GeckoAppShell.getHandler().post(new Runnable() {
                 public void run() {
                     Log.i(LOGTAG, "Checking profile migration in: " + profileDir.getAbsolutePath());
 
-                    ProfileMigrator profileMigrator =
-                        new ProfileMigrator(app, profileDir);
+                    ProfileMigrator profileMigrator = new ProfileMigrator(app);
 
                     // Do a migration run on the first start after an upgrade.
                     if (!profileMigrator.hasMigrationRun()) {
                         // Show the "Setting up Fennec" screen if this takes
                         // a while.
                         final SetupScreen setupScreen = new SetupScreen(app);
 
                         final Runnable startCallback = new Runnable() {
@@ -2268,35 +2267,34 @@ abstract public class GeckoApp
                                         setupScreen.dismiss();
                                     }
                                 });
                             }
                         };
 
                         profileMigrator.setLongOperationCallbacks(startCallback,
                                                                   stopCallback);
-                        profileMigrator.launchPlaces();
+                        profileMigrator.launchPlaces(profileDir);
 
                         long timeDiff = SystemClock.uptimeMillis() - currentTime;
                         Log.i(LOGTAG, "Profile migration took " + timeDiff + " ms");
 
                         // Update about:home with the new information.
                         updateAboutHomeTopSites();
                     }
                 }}
             );
         }
     }
 
     private void checkMigrateSync() {
         final File profileDir = getProfile().getDir();
         if (profileDir != null) {
             final GeckoApp app = GeckoApp.mAppContext;
-            ProfileMigrator profileMigrator =
-                new ProfileMigrator(app, profileDir);
+            ProfileMigrator profileMigrator = new ProfileMigrator(app);
             if (!profileMigrator.hasSyncMigrated()) {
                 Log.i(LOGTAG, "Checking Sync settings in: " + profileDir.getAbsolutePath());
                 profileMigrator.launchSyncPrefs();
             }
         }
     }
 
     /**
--- a/mobile/android/base/GeckoAppShell.java
+++ b/mobile/android/base/GeckoAppShell.java
@@ -370,17 +370,16 @@ public class GeckoAppShell
         }
         catch (Exception e) {
             Log.i(LOGTAG, "No download directory has been found: " + e);
         }
     }
 
     public static void setupGeckoEnvironment(Context context) {
         GeckoProfile profile = GeckoProfile.get(context);
-        profile.moveProfilesToAppInstallLocation();
 
         setupPluginEnvironment((GeckoApp) context);
         setupDownloadEnvironment((GeckoApp) context);
 
         // profile home path
         GeckoAppShell.putenv("HOME=" + profile.getFilesDir().getPath());
 
         Intent i = null;
--- a/mobile/android/base/GeckoProfile.java
+++ b/mobile/android/base/GeckoProfile.java
@@ -5,18 +5,16 @@
  * 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/.
  *
  * ***** END LICENSE BLOCK ***** */
 
 package org.mozilla.gecko;
 
 import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
 import java.io.FileReader;
 import java.io.FileWriter;
 import java.io.IOException;
 import java.nio.channels.FileChannel;
 import java.util.HashMap;
 import android.content.Context;
 import android.os.Build;
 import android.os.SystemClock;
@@ -54,40 +52,64 @@ public final class GeckoProfile {
             if (profile == null) {
                 profile = new GeckoProfile(context, profileName);
                 sProfileCache.put(profileName, profile);
             }
             return profile;
         }
     }
 
+    public static File ensureMozillaDirectory(Context context) throws IOException {
+        synchronized (context) {
+            File filesDir = context.getFilesDir();
+            File mozDir = new File(filesDir, "mozilla");
+            if (! mozDir.exists()) {
+                if (! mozDir.mkdirs()) {
+                    throw new IOException("Unable to create mozilla directory at " + mozDir.getAbsolutePath());
+                }
+            }
+            return mozDir;
+        }
+    }
+
     private GeckoProfile(Context context, String profileName) {
         mContext = context;
         mName = profileName;
     }
 
     public synchronized File getDir() {
         if (mDir != null) {
             return mDir;
         }
 
         try {
-            File mozillaDir = ensureMozillaDirectory();
+            // Check for old profiles that may need migration.
+            ProfileMigrator profileMigrator = new ProfileMigrator(mContext);
+            if (!profileMigrator.isProfileMoved()) {
+                Log.i(LOGTAG, "New installation or update, checking for old profiles.");
+                profileMigrator.launchMoveProfile();
+            }
+
+            File mozillaDir = ensureMozillaDirectory(mContext);
             mDir = findProfileDir(mozillaDir);
             if (mDir == null) {
                 mDir = createProfileDir(mozillaDir);
             } else {
                 Log.d(LOGTAG, "Found profile dir: " + mDir.getAbsolutePath());
             }
         } catch (IOException ioe) {
             Log.e(LOGTAG, "Error getting profile dir", ioe);
         }
         return mDir;
     }
 
+    public File getFilesDir() {
+        return mContext.getFilesDir();
+    }
+
     public boolean shouldRestoreSession() {
         Log.w(LOGTAG, "zerdatime " + SystemClock.uptimeMillis() + " - start check sessionstore.js exists");
         File dir = getDir();
         if (dir == null)
             return false;
 
         File sessionFile = new File(dir, "sessionstore.js");
         if (!sessionFile.exists())
@@ -149,157 +171,16 @@ public final class GeckoProfile {
                 read = fr.read(buf);
             }
             return sb.toString();
         } finally {
             fr.close();
         }
     }
 
-    public File getFilesDir() {
-        return mContext.getFilesDir();
-    }
-
-    private boolean isOnInternalStorage() {
-        // prior to version 8, apps were always on internal storage
-        if (Build.VERSION.SDK_INT < 8) {
-            return true;
-        }
-        // if there is no external storage dir, then we're definitely on internal storage
-        File externalDir = mContext.getExternalFilesDir(null);
-        if (externalDir == null) {
-            return true;
-        }
-        // otherwise, check app install location to see if it is on internal storage
-        String resourcePath = mContext.getPackageResourcePath();
-        if (resourcePath.startsWith("/data") || resourcePath.startsWith("/system")) {
-            return true;
-        }
-
-        // otherwise we're most likely on external storage
-        return false;
-    }
-
-    public void moveProfilesToAppInstallLocation() {
-        // check normal install directory
-        moveProfilesFrom(new File("/data/data/" + mContext.getPackageName()));
-
-        if (Build.VERSION.SDK_INT >= 8) {
-            // if we're on API >= 8, it's possible that
-            // we were previously on external storage, check there for profiles to pull in
-            moveProfilesFrom(mContext.getExternalFilesDir(null));
-        }
-    }
-
-    private void moveProfilesFrom(File oldFilesDir) {
-        if (oldFilesDir == null) {
-            return;
-        }
-        File oldMozDir = new File(oldFilesDir, "mozilla");
-        if (! (oldMozDir.exists() && oldMozDir.isDirectory())) {
-            return;
-        }
-
-        // if we get here, we know that oldMozDir exists
-        File currentMozDir;
-        try {
-            currentMozDir = ensureMozillaDirectory();
-            if (currentMozDir.equals(oldMozDir)) {
-                return;
-            }
-        } catch (IOException ioe) {
-            Log.e(LOGTAG, "Unable to create a profile directory!", ioe);
-            return;
-        }
-
-        Log.d(LOGTAG, "Moving old profile directories from " + oldMozDir.getAbsolutePath());
-
-        // if we get here, we know that oldMozDir != currentMozDir, so we have some stuff to move
-        moveDirContents(oldMozDir, currentMozDir);
-    }
-
-    private void moveDirContents(File src, File dst) {
-        File[] files = src.listFiles();
-        if (files == null) {
-            src.delete();
-            return;
-        }
-        for (File f : files) {
-            File target = new File(dst, f.getName());
-            try {
-                if (f.renameTo(target)) {
-                    continue;
-                }
-            } catch (SecurityException se) {
-                Log.w(LOGTAG, "Unable to rename file to " + target.getAbsolutePath() + " while moving profiles", se);
-            }
-            // rename failed, try moving manually
-            if (f.isDirectory()) {
-                if (target.exists() || target.mkdirs()) {
-                    moveDirContents(f, target);
-                } else {
-                    Log.e(LOGTAG, "Unable to create folder " + target.getAbsolutePath() + " while moving profiles");
-                }
-            } else {
-                if (!moveFile(f, target)) {
-                    Log.e(LOGTAG, "Unable to move file " + target.getAbsolutePath() + " while moving profiles");
-                }
-            }
-        }
-        src.delete();
-    }
-
-    private boolean moveFile(File src, File dst) {
-        boolean success = false;
-        long lastModified = src.lastModified();
-        try {
-            FileInputStream fis = new FileInputStream(src);
-            try {
-                FileOutputStream fos = new FileOutputStream(dst);
-                try {
-                    FileChannel inChannel = fis.getChannel();
-                    long size = inChannel.size();
-                    if (size == inChannel.transferTo(0, size, fos.getChannel())) {
-                        success = true;
-                    }
-                } finally {
-                    fos.close();
-                }
-            } finally {
-                fis.close();
-            }
-        } catch (IOException ioe) {
-            Log.e(LOGTAG, "Exception while attempting to move file to " + dst.getAbsolutePath(), ioe);
-        }
-
-        if (success) {
-            dst.setLastModified(lastModified);
-            src.delete();
-        } else {
-            dst.delete();
-        }
-        return success;
-    }
-
-    private synchronized File ensureMozillaDirectory() throws IOException {
-        if (mMozDir != null) {
-            return mMozDir;
-        }
-
-        File filesDir = getFilesDir();
-        File mozDir = new File(filesDir, "mozilla");
-        if (! mozDir.exists()) {
-            if (! mozDir.mkdirs()) {
-                throw new IOException("Unable to create mozilla directory at " + mozDir.getAbsolutePath());
-            }
-        }
-        mMozDir = mozDir;
-        return mMozDir;
-    }
-
     private File findProfileDir(File mozillaDir) {
         String suffix = '.' + mName;
         File[] candidates = mozillaDir.listFiles();
         if (candidates == null) {
             return null;
         }
         for (File f : candidates) {
             if (f.isDirectory() && f.getName().endsWith(suffix)) {
--- a/mobile/android/base/ProfileMigrator.java
+++ b/mobile/android/base/ProfileMigrator.java
@@ -62,26 +62,30 @@ import android.content.SharedPreferences
 import android.database.Cursor;
 import android.database.SQLException;
 import android.database.sqlite.SQLiteConstraintException;
 import android.graphics.Bitmap;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.os.AsyncTask;
+import android.os.Build;
 import android.os.RemoteException;
 import android.provider.Browser;
 import android.text.TextUtils;
 import android.util.Log;
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.InputStream;
 import java.io.IOException;
-import java.io.File;
-import java.io.InputStream;
+import java.nio.channels.FileChannel;
 import java.util.Arrays;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
@@ -90,17 +94,16 @@ import java.util.Set;
 
 import org.json.JSONArray;
 import org.json.JSONObject;
 import org.json.JSONException;
 
 public class ProfileMigrator {
     private static final String LOGTAG = "ProfileMigrator";
     private static final String PREFS_NAME = "ProfileMigrator";
-    private File mProfileDir;
     private ContentResolver mCr;
     private Context mContext;
     private Runnable mLongOperationStartCallback;
     private boolean mLongOperationStartRun;
     private Runnable mLongOperationStopCallback;
 
     // Default number of history entries to migrate in one run.
     private static final int DEFAULT_HISTORY_MIGRATE_COUNT = 2000;
@@ -110,16 +113,20 @@ public class ProfileMigrator {
     private static final int HISTORY_MAX_BATCH = 5000;
 
     private static final String PREFS_MIGRATE_BOOKMARKS_DONE = "bookmarks_done";
     private static final String PREFS_MIGRATE_HISTORY_DONE = "history_done";
     // Number of history entries already migrated.
     private static final String PREFS_MIGRATE_HISTORY_COUNT = "history_count";
     private static final String PREFS_MIGRATE_SYNC_DONE = "sync_done";
 
+    // Profile has been moved to internal storage?
+    private static final String PREFS_MIGRATE_MOVE_PROFILE_DONE
+        = "move_profile_done";
+
     /*
        These queries are derived from the low-level Places schema
        https://developer.mozilla.org/en/The_Places_database
     */
     private static final String ROOT_QUERY =
         "SELECT root_name, folder_id FROM moz_bookmarks_roots";
     private static final String ROOT_NAME      = "root_name";
     private static final String ROOT_FOLDER_ID = "folder_id";
@@ -266,71 +273,81 @@ public class ProfileMigrator {
     */
     private static final String SYNC_HOST_NAME = "chrome://weave";
     private static final String[] SYNC_REALM_LIST = new String[] {
         "Mozilla Services Password",
         "Mozilla Services Encryption Passphrase"
     };
 
 
-    public ProfileMigrator(Context context, File profileDir) {
-        mProfileDir = profileDir;
+    public ProfileMigrator(Context context) {
         mContext = context;
         mCr = mContext.getContentResolver();
         mLongOperationStartCallback = null;
         mLongOperationStopCallback = null;
     }
 
     // Define callbacks to run if the operation will take a while.
     // Stop callback is only run if there was a start callback that was run.
     public void setLongOperationCallbacks(Runnable start,
                                           Runnable stop) {
         mLongOperationStartCallback = start;
         mLongOperationStopCallback = stop;
         mLongOperationStartRun = false;
     }
 
-    public void launchPlaces() {
+    public void launchPlaces(File profileDir) {
         boolean timeThisRun = false;
         Telemetry.Timer timer = null;
         // First run, time things
         if (!hasMigrationRun()) {
             timeThisRun = true;
             timer = new Telemetry.Timer("BROWSERPROVIDER_XUL_IMPORT_TIME");
         }
-        launchPlaces(DEFAULT_HISTORY_MIGRATE_COUNT);
+        launchPlaces(profileDir, DEFAULT_HISTORY_MIGRATE_COUNT);
         if (timeThisRun)
             timer.stop();
     }
 
-    public void launchPlaces(int maxEntries) {
+    public void launchPlaces(File profileDir, int maxEntries) {
         mLongOperationStartRun = false;
         // Places migration is heavy on the phone, allow it to block
         // other processing.
-        new PlacesRunnable(maxEntries).run();
+        new PlacesRunnable(profileDir, maxEntries).run();
     }
 
     public void launchSyncPrefs() {
         // Sync settings will post a runnable, no need for a seperate thread.
         new SyncTask().run();
     }
 
+    public void launchMoveProfile() {
+        // Make sure the profile is on internal storage.
+        new MoveProfileTask().run();
+    }
+
     public boolean areBookmarksMigrated() {
         return getPreferences().getBoolean(PREFS_MIGRATE_BOOKMARKS_DONE, false);
     }
 
     public boolean isHistoryMigrated() {
         return getPreferences().getBoolean(PREFS_MIGRATE_HISTORY_DONE, false);
     }
 
     // Have Sync settings been transferred?
     public boolean hasSyncMigrated() {
         return getPreferences().getBoolean(PREFS_MIGRATE_SYNC_DONE, false);
     }
 
+    // Has the profile been moved from an SDcard to internal storage?
+    public boolean isProfileMoved() {
+        return getPreferences().getBoolean(PREFS_MIGRATE_MOVE_PROFILE_DONE,
+                                           false);
+    }
+
     // Has migration run before?
     protected boolean hasMigrationRun() {
         return areBookmarksMigrated()
             && ((getMigratedHistoryEntries() > 0) || isHistoryMigrated());
     }
 
     // Has migration entirely finished?
     protected boolean hasMigrationFinished() {
@@ -346,32 +363,148 @@ public class ProfileMigrator {
     }
 
     protected void setMigratedHistoryEntries(int count) {
         SharedPreferences.Editor editor = getPreferences().edit();
         editor.putInt(PREFS_MIGRATE_HISTORY_COUNT, count);
         editor.commit();
     }
 
-    protected void setMigratedHistory() {
+    protected void setBooleanPrefTrue(String prefName) {
         SharedPreferences.Editor editor = getPreferences().edit();
-        editor.putBoolean(PREFS_MIGRATE_HISTORY_DONE, true);
+        editor.putBoolean(prefName, true);
         editor.commit();
     }
 
+    protected void setMigratedHistory() {
+        setBooleanPrefTrue(PREFS_MIGRATE_BOOKMARKS_DONE);
+    }
+
     protected void setMigratedBookmarks() {
-        SharedPreferences.Editor editor = getPreferences().edit();
-        editor.putBoolean(PREFS_MIGRATE_BOOKMARKS_DONE, true);
-        editor.commit();
+        setBooleanPrefTrue(PREFS_MIGRATE_BOOKMARKS_DONE);
     }
 
     protected void setMigratedSync() {
-        SharedPreferences.Editor editor = getPreferences().edit();
-        editor.putBoolean(PREFS_MIGRATE_SYNC_DONE, true);
-        editor.commit();
+        setBooleanPrefTrue(PREFS_MIGRATE_SYNC_DONE);
+    }
+
+    protected void setMovedProfile() {
+        setBooleanPrefTrue(PREFS_MIGRATE_MOVE_PROFILE_DONE);
+    }
+
+    private class MoveProfileTask implements Runnable {
+
+        protected void moveProfilesToAppInstallLocation() {
+            if (Build.VERSION.SDK_INT >= 8) {
+                // if we're on API >= 8, it's possible that
+                // we were previously on external storage, check there for profiles to pull in
+                moveProfilesFrom(mContext.getExternalFilesDir(null));
+            }
+
+            // Maybe it worked. Maybe it didn't. We won't try again.
+            setMovedProfile();
+        }
+
+        protected void moveProfilesFrom(File oldFilesDir) {
+            if (oldFilesDir == null) {
+                return;
+            }
+            File oldMozDir = new File(oldFilesDir, "mozilla");
+            if (! (oldMozDir.exists() && oldMozDir.isDirectory())) {
+                return;
+            }
+
+            // if we get here, we know that oldMozDir exists
+            File currentMozDir;
+            try {
+                currentMozDir = GeckoProfile.ensureMozillaDirectory(mContext);
+                if (currentMozDir.equals(oldMozDir)) {
+                    return;
+                }
+            } catch (IOException ioe) {
+                Log.e(LOGTAG, "Unable to create a profile directory!", ioe);
+                return;
+            }
+
+            Log.d(LOGTAG, "Moving old profile directories from " + oldMozDir.getAbsolutePath());
+
+            // if we get here, we know that oldMozDir != currentMozDir, so we have some stuff to move
+            moveDirContents(oldMozDir, currentMozDir);
+    }
+
+        protected void moveDirContents(File src, File dst) {
+            File[] files = src.listFiles();
+            if (files == null) {
+                src.delete();
+                return;
+            }
+            for (File f : files) {
+                File target = new File(dst, f.getName());
+                try {
+                    if (f.renameTo(target)) {
+                        continue;
+                    }
+                } catch (SecurityException se) {
+                    Log.w(LOGTAG, "Unable to rename file to " + target.getAbsolutePath() + " while moving profiles", se);
+                }
+                // rename failed, try moving manually
+                if (f.isDirectory()) {
+                    if (target.exists() || target.mkdirs()) {
+                        moveDirContents(f, target);
+                    } else {
+                        Log.e(LOGTAG, "Unable to create folder " + target.getAbsolutePath() + " while moving profiles");
+                    }
+                } else {
+                    if (!moveFile(f, target)) {
+                        Log.e(LOGTAG, "Unable to move file " + target.getAbsolutePath() + " while moving profiles");
+                    }
+                }
+            }
+            src.delete();
+        }
+
+        protected boolean moveFile(File src, File dst) {
+            boolean success = false;
+            long lastModified = src.lastModified();
+            try {
+                FileInputStream fis = new FileInputStream(src);
+                try {
+                    FileOutputStream fos = new FileOutputStream(dst);
+                    try {
+                        FileChannel inChannel = fis.getChannel();
+                        long size = inChannel.size();
+                        if (size == inChannel.transferTo(0, size, fos.getChannel())) {
+                            success = true;
+                        }
+                    } finally {
+                        fos.close();
+                    }
+                } finally {
+                    fis.close();
+                }
+            } catch (IOException ioe) {
+                Log.e(LOGTAG, "Exception while attempting to move file to " + dst.getAbsolutePath(), ioe);
+            }
+
+            if (success) {
+                dst.setLastModified(lastModified);
+                src.delete();
+            } else {
+                dst.delete();
+            }
+            return success;
+        }
+
+        @Override
+        public void run() {
+            if (isProfileMoved()) {
+                return;
+            }
+            moveProfilesToAppInstallLocation();
+        }
     }
 
     private class SyncTask implements Runnable, GeckoEventListener {
         private List<String> mSyncSettingsList;
         private Map<String, String> mSyncSettingsMap;
 
         // Initialize preferences by sending the "Preferences:Get" command to Gecko
         protected void requestValues() {
@@ -577,26 +710,28 @@ public class ProfileMigrator {
         @Override
         public void run() {
             // XXX: Land dependent bugs (732069) first
             // cleanupXULLibCache();
         }
     }
 
     private class PlacesRunnable implements Runnable {
+        private File mProfileDir;
         private Map<Long, Long> mRerootMap;
         private Long mTagsPlacesFolderId;
         private ArrayList<ContentProviderOperation> mOperations;
         private int mMaxEntries;
         // We support 2 classes of schemas: Firefox Places 12-13
         // and Firefox Places 13-20. The relevant difference for us
         // is whether there is a GUID on favicons or not.
         private boolean mHasFaviconGUID;
 
-        public PlacesRunnable(int limit) {
+        public PlacesRunnable(File profileDir, int limit) {
+            mProfileDir = profileDir;
             mMaxEntries = limit;
         }
 
         protected Uri getBookmarksUri() {
             Uri.Builder uriBuilder = Bookmarks.CONTENT_URI.buildUpon()
                 .appendQueryParameter(BrowserContract.PARAM_SHOW_DELETED, "1");
             return uriBuilder.build();
         }
--- a/mobile/android/base/db/BrowserProvider.java.in
+++ b/mobile/android/base/db/BrowserProvider.java.in
@@ -1496,24 +1496,23 @@ public class BrowserProvider extends Con
                 if (key.equals(Control.ENSURE_BOOKMARKS_MIGRATED)) {
                     wantBookmarks = true;
                 } else if (key.equals(Control.ENSURE_HISTORY_MIGRATED)) {
                     wantHistory = true;
                 }
             }
 
             if (wantHistory || wantBookmarks) {
-                ProfileMigrator migrator =
-                    new ProfileMigrator(mContext, profileDir);
+                ProfileMigrator migrator = new ProfileMigrator(mContext);
 
                 boolean needBookmarks = wantBookmarks && !migrator.areBookmarksMigrated();
                 boolean needHistory = wantHistory && !migrator.isHistoryMigrated();
 
                 if (needBookmarks || needHistory) {
-                    migrator.launchPlaces();
+                    migrator.launchPlaces(profileDir);
 
                     needBookmarks = wantBookmarks && !migrator.areBookmarksMigrated();
                     needHistory = wantHistory && !migrator.isHistoryMigrated();
                     // Bookmarks are expected to finish at the first run.
                     if (needBookmarks) {
                         Log.w(LOGTAG, "Bookmarks migration did not finish.");
                     }
                 }