author | Kartikaya Gupta <kgupta@mozilla.com> |
Sat, 25 Feb 2012 23:22:40 -0500 | |
changeset 87756 | 92362d0c00e4ae87ce9cc07435ea2b7542328ef1 |
parent 87755 | 0fb823decef1b362e789bfeb55a4170e7455c9f4 |
child 87757 | 0dc734db4a13cd9b29cf4cec4b88d1e6c3f8de09 |
push id | 22143 |
push user | philringnalda@gmail.com |
push date | Sun, 26 Feb 2012 23:12:35 +0000 |
treeherder | mozilla-central@b98fc24ac54b [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | wesj |
bugs | 726382 |
milestone | 13.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/mobile/android/base/AboutHomeContent.java +++ b/mobile/android/base/AboutHomeContent.java @@ -362,35 +362,16 @@ public class AboutHomeContent extends Sc if (mTopSitesGrid != null) mTopSitesGrid.setNumColumns(getNumberOfColumns()); if (mTopSitesAdapter != null) mTopSitesAdapter.notifyDataSetChanged(); super.onConfigurationChanged(newConfig); } - private String readJSONFile(Activity activity, String filename) { - InputStream fileStream = null; - File profileDir = GeckoApp.mAppContext.getProfileDir(); - - if (profileDir == null) - return null; - - File recommendedAddonsFile = new File(profileDir, filename); - if (recommendedAddonsFile.exists()) { - try { - fileStream = new FileInputStream(recommendedAddonsFile); - } catch (FileNotFoundException fnfe) {} - } - if (fileStream == null) - return null; - - return readStringFromStream(fileStream); - } - private String readFromZipFile(Activity activity, String filename) { ZipFile zip = null; String str = null; try { InputStream fileStream = null; File applicationPackage = new File(activity.getApplication().getPackageResourcePath()); zip = new ZipFile(applicationPackage); if (zip == null) @@ -454,19 +435,21 @@ public class AboutHomeContent extends Sc // Defaults to pageUrl = iconUrl in case of error } return pageUrl; } private void readRecommendedAddons(final Activity activity) { final String addonsFilename = "recommended-addons.json"; - String jsonString = readJSONFile(activity, addonsFilename); - if (jsonString == null) { - Log.i("Addons", "filestream is null"); + String jsonString; + try { + jsonString = GeckoApp.mAppContext.getProfile().readFile(addonsFilename); + } catch (IOException ioe) { + Log.i(LOGTAG, "filestream is null"); jsonString = readFromZipFile(activity, addonsFilename); } JSONArray addonsArray = null; if (jsonString != null) { try { addonsArray = new JSONObject(jsonString).getJSONArray("addons"); } catch (JSONException e) { @@ -519,43 +502,23 @@ public class AboutHomeContent extends Sc } catch (JSONException e) { Log.i(LOGTAG, "error reading json file", e); } } }); } private void readLastTabs(final Activity activity) { - final String sessionFilename; - if (!GeckoApp.sIsGeckoReady) { - File profileDir = GeckoApp.mAppContext.getProfileDir(); - if (profileDir == null) - return; - - if (new File(profileDir, "sessionstore.js").exists()) { - // we crashed, so sessionstore.js has tabs from last time - sessionFilename = "sessionstore.js"; - } else if (new File(profileDir, "sessionstore.bak").exists()) { - // we did not crash, so previous session was moved to sessionstore.bak on quit - sessionFilename = "sessionstore.bak"; - } else { - // no previous session data - return; - } - } else { - // sessionstore init has occurred, so previous session will always - // be in sessionstore.bak - sessionFilename = "sessionstore.bak"; + String jsonString = GeckoApp.mAppContext.getProfile().readSessionFile(GeckoApp.sIsGeckoReady); + if (jsonString == null) { + // no previous session data + return; } final JSONArray tabs; - String jsonString = readJSONFile(activity, sessionFilename); - if (jsonString == null) - return; - try { tabs = new JSONObject(jsonString).getJSONArray("windows") .getJSONObject(0) .getJSONArray("tabs"); } catch (JSONException e) { Log.i(LOGTAG, "error reading json file", e); return; }
--- a/mobile/android/base/GeckoApp.java +++ b/mobile/android/base/GeckoApp.java @@ -120,17 +120,17 @@ abstract public class GeckoApp private RelativeLayout mGeckoLayout; public static SurfaceView cameraView; public static GeckoApp mAppContext; public static boolean mDOMFullScreen = false; public static File sGREDir = null; public static Menu sMenu; private static GeckoThread sGeckoThread = null; public GeckoAppHandler mMainHandler; - private File mProfileDir; + private GeckoProfile mProfile; public static boolean sIsGeckoReady = false; public static int mOrientation; private IntentFilter mConnectivityFilter; private BroadcastReceiver mConnectivityReceiver; private BroadcastReceiver mBatteryReceiver; @@ -844,31 +844,16 @@ abstract public class GeckoApp settings.edit().putString(keyName, appVersion).commit(); Log.i(LOGTAG, "Startup mode: " + mStartupMode); return mStartupMode; } } - public File getProfileDir() { - return getProfileDir("default"); - } - - public File getProfileDir(final String profileName) { - if (mProfileDir != null) - return mProfileDir; - try { - mProfileDir = GeckoDirProvider.getProfileDir(mAppContext, profileName); - } catch (IOException ex) { - Log.e(LOGTAG, "Error getting profile dir.", ex); - } - return mProfileDir; - } - void addTab() { showAwesomebar(AwesomeBar.Type.ADD); } void showTabs() { Intent intent = new Intent(mAppContext, TabsTray.class); intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); startActivity(intent); @@ -1709,17 +1694,17 @@ abstract public class GeckoApp mInitialized = true; Intent intent = getIntent(); String args = intent.getStringExtra("args"); if (args != null && args.contains("-profile")) { Pattern p = Pattern.compile("(?:-profile\\s*)(\\w*)(\\s*)"); Matcher m = p.matcher(args); if (m.find()) { - mProfileDir = new File(m.group(1)); + mProfile = GeckoProfile.get(this, m.group(1)); mLastTitle = null; mLastViewport = null; mLastScreen = null; } } if (ACTION_UPDATE.equals(intent.getAction()) || args != null && args.contains("-alert update-app")) { Log.i(LOGTAG,"onCreate: Update request"); @@ -1731,23 +1716,17 @@ abstract public class GeckoApp String passedUri = null; String uri = getURIFromIntent(intent); if (uri != null && uri.length() > 0) passedUri = mLastTitle = uri; if (passedUri == null || passedUri.equals("about:home")) { // show about:home if we aren't restoring previous session - Log.w(LOGTAG, "zerdatime " + SystemClock.uptimeMillis() + " - start check sessionstore.js exists"); - File profileDir = getProfileDir(); - boolean sessionExists = false; - if (profileDir != null) - sessionExists = new File(profileDir, "sessionstore.js").exists(); - Log.w(LOGTAG, "zerdatime " + SystemClock.uptimeMillis() + " - finish check sessionstore.js exists"); - if (!sessionExists) { + if (! getProfile().hasSession()) { mBrowserToolbar.updateTabCount(1); showAboutHome(); } } else { mBrowserToolbar.updateTabCount(1); } if (sGREDir == null) @@ -1887,16 +1866,24 @@ abstract public class GeckoApp return; } checkMigrateProfile(); } }, 50); } + public GeckoProfile getProfile() { + // fall back to default profile if we didn't load a specific one + if (mProfile == null) { + mProfile = GeckoProfile.get(this); + } + return mProfile; + } + /** * Enable Android StrictMode checks (for supported OS versions). * http://developer.android.com/reference/android/os/StrictMode.html */ private void enableStrictMode() { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.GINGERBREAD) { return; @@ -2366,17 +2353,17 @@ abstract public class GeckoApp reader.close(); } catch (Exception e) { Log.i(LOGTAG, "error reading update status", e); } return status; } private void checkMigrateProfile() { - File profileDir = getProfileDir(); + File profileDir = getProfile().getDir(); long currentTime = SystemClock.uptimeMillis(); if (profileDir != null) { Log.i(LOGTAG, "checking profile migration in: " + profileDir.getAbsolutePath()); final GeckoApp app = GeckoApp.mAppContext; final SetupScreen setupScreen = new SetupScreen(app); // don't show unless we take a while setupScreen.showDelayed(mMainHandler);
--- a/mobile/android/base/GeckoAppShell.java +++ b/mobile/android/base/GeckoAppShell.java @@ -41,17 +41,16 @@ package org.mozilla.gecko; import org.mozilla.gecko.gfx.BitmapUtils; import org.mozilla.gecko.gfx.GeckoSoftwareLayerClient; import org.mozilla.gecko.gfx.LayerController; import org.mozilla.gecko.gfx.LayerView; import java.io.*; import java.lang.reflect.*; import java.nio.*; -import java.nio.channels.*; import java.text.*; import java.util.*; import java.util.zip.*; import java.util.concurrent.*; import android.os.*; import android.app.*; import android.text.*; @@ -258,130 +257,39 @@ public class GeckoAppShell } } } catch (Exception e) { Log.e(LOGTAG, "exception while stating cache dir: ", e); } return sFreeSpace; } - static boolean moveFile(File inFile, File outFile) - { - Log.i(LOGTAG, "moving " + inFile + " to " + outFile); - if (outFile.isDirectory()) - outFile = new File(outFile, inFile.getName()); - try { - if (inFile.renameTo(outFile)) - return true; - } catch (SecurityException se) { - Log.w(LOGTAG, "error trying to rename file", se); - } - try { - long lastModified = inFile.lastModified(); - outFile.createNewFile(); - // so copy it instead - FileChannel inChannel = new FileInputStream(inFile).getChannel(); - FileChannel outChannel = new FileOutputStream(outFile).getChannel(); - long size = inChannel.size(); - long transferred = inChannel.transferTo(0, size, outChannel); - inChannel.close(); - outChannel.close(); - outFile.setLastModified(lastModified); - - if (transferred == size) - inFile.delete(); - else - return false; - } catch (Exception e) { - Log.e(LOGTAG, "exception while moving file: ", e); - try { - outFile.delete(); - } catch (SecurityException se) { - Log.w(LOGTAG, "error trying to delete file", se); - } - return false; - } - return true; - } - - static boolean moveDir(File from, File to) { - try { - to.mkdirs(); - if (from.renameTo(to)) - return true; - } catch (SecurityException se) { - Log.w(LOGTAG, "error trying to rename file", se); - } - File[] files = from.listFiles(); - boolean retVal = true; - if (files == null) - return false; - try { - Iterator<File> fileIterator = Arrays.asList(files).iterator(); - while (fileIterator.hasNext()) { - File file = fileIterator.next(); - File dest = new File(to, file.getName()); - if (file.isDirectory()) - retVal = moveDir(file, dest) ? retVal : false; - else - retVal = moveFile(file, dest) ? retVal : false; - } - from.delete(); - } catch(Exception e) { - Log.e(LOGTAG, "error trying to move file", e); - } - return retVal; - } - // java-side stuff public static boolean loadLibsSetup(String apkName) { // The package data lib directory isn't placed in ld.so's // search path, so we have to manually load libraries that // libxul will depend on. Not ideal. GeckoApp geckoApp = GeckoApp.mAppContext; - String homeDir; - sHomeDir = GeckoDirProvider.getFilesDir(geckoApp); - homeDir = sHomeDir.getPath(); - - // handle the application being moved to phone from sdcard - File profileDir = new File(homeDir, "mozilla"); - File oldHome = new File("/data/data/" + - GeckoApp.mAppContext.getPackageName() + "/mozilla"); - if (oldHome.exists()) - moveDir(oldHome, profileDir); + GeckoProfile profile = geckoApp.getProfile(); + profile.moveProfilesToAppInstallLocation(); - if (Build.VERSION.SDK_INT < 8 || - geckoApp.getApplication().getPackageResourcePath().startsWith("/data") || - geckoApp.getApplication().getPackageResourcePath().startsWith("/system")) { - if (Build.VERSION.SDK_INT >= 8) { - File extHome = geckoApp.getExternalFilesDir(null); - File extProf = new File (extHome, "mozilla"); - if (extHome != null && extProf != null && extProf.exists()) - moveDir(extProf, profileDir); - } - } else { - File intHome = geckoApp.getFilesDir(); - File intProf = new File(intHome, "mozilla"); - if (intHome != null && intProf != null && intProf.exists()) - moveDir(intProf, profileDir); - } try { String[] dirs = GeckoApp.mAppContext.getPluginDirectories(); StringBuffer pluginSearchPath = new StringBuffer(); for (int i = 0; i < dirs.length; i++) { Log.i(LOGTAG, "dir: " + dirs[i]); pluginSearchPath.append(dirs[i]); pluginSearchPath.append(":"); } GeckoAppShell.putenv("MOZ_PLUGIN_PATH="+pluginSearchPath); } catch (Exception ex) { Log.i(LOGTAG, "exception getting plugin dirs", ex); } - GeckoAppShell.putenv("HOME=" + homeDir); + GeckoAppShell.putenv("HOME=" + profile.getFilesDir().getPath()); GeckoAppShell.putenv("GRE_HOME=" + GeckoApp.sGREDir.getPath()); Intent i = geckoApp.getIntent(); String env = i.getStringExtra("env0"); Log.i(LOGTAG, "env0: "+ env); for (int c = 1; env != null; c++) { GeckoAppShell.putenv(env); env = i.getStringExtra("env" + c); Log.i(LOGTAG, "env"+ c +": "+ env);
deleted file mode 100644 --- a/mobile/android/base/GeckoDirProvider.java +++ /dev/null @@ -1,215 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Android code. - * - * The Initial Developer of the Original Code is Mozilla Foundation. - * Portions created by the Initial Developer are Copyright (C) 2009-2010 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Wes Johnston <wjohnston@mozilla.com> - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -package org.mozilla.gecko; - -import android.content.Context; -import android.os.AsyncTask; -import android.os.Build; -import android.util.Log; - -import java.io.File; -import java.io.FileFilter; -import java.io.FileWriter; -import java.io.IOException; -import java.lang.Void; -import java.util.Date; -import java.util.Random; -import java.util.Map; -import java.util.HashMap; - -abstract public class GeckoDirProvider -{ - private static final String LOGTAG = "GeckoDirProvider"; - private static HashMap<String, File> mProfileDirs = new HashMap<String, File>(); - - /** - * Get the default Mozilla profile directory for a given Activity instance. - * - * @param aContext - * The context for the activity. Must not be null - * @return - * The profile directory. - */ - static public File getProfileDir(final Context aContext) - throws IllegalArgumentException, IOException { - // XXX: TO-DO read profiles.ini to get the default profile. bug 71530 - return getProfileDir(aContext, "default"); - } - - /** - * Get a particular profile directory for a given Activity. - * If no profile directory currently exists, will create and return a profile directory. - * Otherwise will return null; - * - * @param aContext - * The context for the Activity we want a profile for. Must not be null. - * @param aProfileName - * The name of the profile to open. Must be a non-empty string - * @return - * The profile directory. - */ - static public File getProfileDir(final Context aContext, final String aProfileName) - throws IllegalArgumentException, IOException { - - if (aContext == null) - throw new IllegalArgumentException("Must provide a valid context"); - - if (aProfileName == null || aProfileName.trim().equals("")) - throw new IllegalArgumentException("Profile name: '" + aProfileName + "' is not valid"); - - Log.i(LOGTAG, "Get profile dir for " + aProfileName); - synchronized (mProfileDirs) { - File profileDir = mProfileDirs.get(aProfileName); - if (profileDir != null) - return profileDir; - - // we do not want to call File.exists on startup, so we first don't - // attempt to create the mozilla directory. - File mozDir = GeckoDirProvider.ensureMozillaDirectory(aContext); - profileDir = GeckoDirProvider.getProfileDir(mozDir, aProfileName); - - if (profileDir == null) { - // Throws if cannot create. - profileDir = GeckoDirProvider.createProfileDir(mozDir, aProfileName); - } - mProfileDirs.put(aProfileName, profileDir); - return profileDir; - } - } - - private static File getProfileDir(final File aRoot, final String aProfileName) - throws IllegalArgumentException { - if (aRoot == null) - throw new IllegalArgumentException("Invalid root directory"); - - File[] profiles = aRoot.listFiles(new FileFilter() { - public boolean accept(File pathname) { - return pathname.getName().endsWith("." + aProfileName); - } - }); - - if (profiles != null && profiles.length > 0) - return profiles[0]; - return null; - } - - private static File ensureMozillaDirectory(final Context aContext) - throws IOException, IllegalArgumentException { - if (aContext == null) - throw new IllegalArgumentException("Must provide a valid context"); - File filesDir = GeckoDirProvider.getFilesDir(aContext); - - File mozDir = new File(filesDir, "mozilla"); - if (!mozDir.exists()) { - if (!mozDir.mkdir()) - throw new IOException("Unable to create mozilla directory at " + mozDir.getPath()); - } - return mozDir; - } - - static final char kTable[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', - 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - '1', '2', '3', '4', '5', '6', '7', '8', '9', '0' }; - - private static File createProfileDir(final File aRootDir, final String aProfileName) - throws IOException, IllegalArgumentException { - - if (aRootDir == null) - throw new IllegalArgumentException("Must provide a valid root directory"); - - if (aProfileName == null || aProfileName.trim().equals("")) - throw new IllegalArgumentException("Profile name: '" + aProfileName + "' is not valid"); - - // XXX: TO-DO If we already have an ini file, we should append the - // new profile information to it. For now we just throw an exception. - // see bug 715391 - final File profileIni = new File(aRootDir, "profiles.ini"); - if (profileIni.exists()) - throw new IOException("Can't create new profiles"); - - String saltedName = saltProfileName(aProfileName); - File profile = new File(aRootDir, saltedName); - while (profile.exists()) { - saltedName = saltProfileName(aProfileName); - profile = new File(aRootDir, saltedName); - } - - if (!profile.mkdir()) - throw new IOException("Unable to create profile at " + profile.getPath()); - - Log.i(LOGTAG, "Creating new profile at " + profile.getPath()); - final String fSaltedName = saltedName; - - FileWriter outputStream = new FileWriter(profileIni, true); - outputStream.write("[General]\n" + - "StartWithLastProfile=1\n" + - "\n" + - "[Profile0]\n" + - "Name=" + aProfileName + "\n" + - "IsRelative=1\n" + - "Path=" + fSaltedName + "\n" + - "Default=1\n"); - outputStream.close(); - - return profile; - } - - private static String saltProfileName(final String aName) { - Random randomGenerator = new Random(System.nanoTime()); - - StringBuilder salt = new StringBuilder(); - int i; - for (i = 0; i < 8; ++i) - salt.append(kTable[randomGenerator.nextInt(kTable.length)]); - - salt.append("."); - return salt.append(aName).toString(); - } - - public static File getFilesDir(final Context aContext) { - if (aContext == null) - throw new IllegalArgumentException("Must provide a valid context"); - - if (Build.VERSION.SDK_INT < 8 || - aContext.getPackageResourcePath().startsWith("/data") || - aContext.getPackageResourcePath().startsWith("/system")) { - return aContext.getFilesDir(); - } - return aContext.getExternalFilesDir(null); - } -}
new file mode 100644 --- /dev/null +++ b/mobile/android/base/GeckoProfile.java @@ -0,0 +1,356 @@ +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- + * ***** BEGIN LICENSE BLOCK ***** + * + * 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/. + * + * ***** 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; +import android.text.TextUtils; +import android.util.Log; + +public final class GeckoProfile { + private static final String LOGTAG = "GeckoProfile"; + + private static HashMap<String, GeckoProfile> sProfileCache = new HashMap<String, GeckoProfile>(); + + private final Context mContext; + private final String mName; + private File mMozDir; + private File mDir; + + public static GeckoProfile get(Context context) { + return get(context, null); + } + + public static GeckoProfile get(Context context, String profileName) { + if (context == null) { + throw new IllegalArgumentException("context must be non-null"); + } + if (TextUtils.isEmpty(profileName)) { + // XXX: TO-DO read profiles.ini to get the default profile. bug 715307 + profileName = "default"; + } + + synchronized (sProfileCache) { + GeckoProfile profile = sProfileCache.get(profileName); + if (profile == null) { + profile = new GeckoProfile(context, profileName); + sProfileCache.put(profileName, profile); + } + return profile; + } + } + + private GeckoProfile(Context context, String profileName) { + mContext = context; + mName = profileName; + } + + public File getDir() { + if (mDir != null) { + return mDir; + } + + try { + File mozillaDir = ensureMozillaDirectory(); + 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 boolean hasSession() { + Log.w(LOGTAG, "zerdatime " + SystemClock.uptimeMillis() + " - start check sessionstore.js exists"); + File dir = getDir(); + boolean hasSession = (dir != null && new File(dir, "sessionstore.js").exists()); + Log.w(LOGTAG, "zerdatime " + SystemClock.uptimeMillis() + " - finish check sessionstore.js exists"); + return hasSession; + } + + public String readSessionFile(boolean geckoReady) { + File dir = getDir(); + if (dir == null) { + return null; + } + + File sessionFile = null; + if (! geckoReady) { + // we might have crashed, in which case sessionstore.js has tabs from last time + sessionFile = new File(dir, "sessionstore.js"); + if (! sessionFile.exists()) { + sessionFile = null; + } + } + if (sessionFile == null) { + // either we did not crash, so previous session was moved to sessionstore.bak on quit, + // or sessionstore init has occurred, so previous session will always + // be in sessionstore.bak + sessionFile = new File(dir, "sessionstore.bak"); + // no need to check if the session file exists here; readFile will throw + // an IOException if it does not + } + + try { + return readFile(sessionFile); + } catch (IOException ioe) { + Log.i(LOGTAG, "Unable to read session file " + sessionFile.getAbsolutePath()); + return null; + } + } + + public String readFile(String filename) throws IOException { + File dir = getDir(); + if (dir == null) { + throw new IOException("No profile directory found"); + } + File target = new File(dir, filename); + return readFile(target); + } + + private String readFile(File target) throws IOException { + FileReader fr = new FileReader(target); + try { + StringBuffer sb = new StringBuffer(); + char[] buf = new char[8192]; + int read = fr.read(buf); + while (read >= 0) { + sb.append(buf, 0, read); + read = fr.read(buf); + } + return sb.toString(); + } finally { + fr.close(); + } + } + + public File getFilesDir() { + if (isOnInternalStorage()) { + return mContext.getFilesDir(); + } else { + return mContext.getExternalFilesDir(null); + } + } + + private boolean isOnInternalStorage() { + // prior to version 8, apps were always on internal storage + if (Build.VERSION.SDK_INT < 8) { + 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 (isOnInternalStorage()) { + if (Build.VERSION.SDK_INT >= 8) { + // if we're currently on internal storage, but we're on API >= 8, so it's possible that + // we were previously on external storage, check there for profiles to pull in + moveProfilesFrom(mContext.getExternalFilesDir(null)); + } + } else { + // we're currently on external storage, but could have been on internal storage previously, + // so pull in those profiles + moveProfilesFrom(mContext.getFilesDir()); + } + } + + 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.e(LOGTAG, "Unable to rename file to " + target.getAbsolutePath() + " while moving profiles", se); + } + // rename failed, try moving manually + if (f.isDirectory()) { + if (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 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)) { + return f; + } + } + return null; + } + + private static String saltProfileName(String name) { + String allowedChars = "abcdefghijklmnopqrstuvwxyz0123456789"; + StringBuffer salt = new StringBuffer(16); + for (int i = 0; i < 8; i++) { + salt.append(allowedChars.charAt((int)(Math.random() * allowedChars.length()))); + } + salt.append('.'); + salt.append(name); + return salt.toString(); + } + + private File createProfileDir(File mozillaDir) throws IOException { + // XXX: TO-DO If we already have an ini file, we should append the + // new profile information to it. For now we just throw an exception. + // see bug 715391 + File profileIniFile = new File(mozillaDir, "profiles.ini"); + if (profileIniFile.exists()) { + throw new IOException("Can't create new profiles"); + } + + String saltedName = saltProfileName(mName); + File profileDir = new File(mozillaDir, saltedName); + while (profileDir.exists()) { + saltedName = saltProfileName(mName); + profileDir = new File(mozillaDir, saltedName); + } + + if (! profileDir.mkdirs()) { + throw new IOException("Unable to create profile at " + profileDir.getAbsolutePath()); + } + Log.d(LOGTAG, "Created new profile dir at " + profileDir.getAbsolutePath()); + + FileWriter out = new FileWriter(profileIniFile, true); + try { + out.write("[General]\n" + + "StartWithLastProfile=1\n" + + "\n" + + "[Profile0]\n" + + "Name=" + mName + "\n" + + "IsRelative=1\n" + + "Path=" + saltedName + "\n" + + "Default=1\n"); + } finally { + out.close(); + } + + return profileDir; + } +}
--- a/mobile/android/base/LauncherShortcuts.java.in +++ b/mobile/android/base/LauncherShortcuts.java.in @@ -142,57 +142,25 @@ public class LauncherShortcuts extends A private class FetchWebApps extends AsyncTask<Void, Void, Void> { @Override protected Void doInBackground(Void... unused) { mWebappsList = null; Context context = getApplicationContext(); - - File home = new File(context.getFilesDir(), "mozilla"); - if (!home.exists()) - home = new File(context.getExternalFilesDir(null).getPath(), "mozilla"); - - if (!home.exists()) - return null; - - File profile = null; - String[] files = home.list(); - for (String file : files) { - if (file.endsWith(".default")) { - profile = new File(home, file); - break; - } + GeckoProfile profile = GeckoProfile.get(context); + String webappsJson = null; + try { + webappsJson = profile.readFile("webapps" + File.separatorChar + "webapps.json"); + } catch (IOException ioe) { + // unable to load the file, leave webappsJson as null } - if (profile == null) - return null; - - // Save the folder path to be used during click event - mWebappsFolder = new File(profile, "webapps"); - if (!mWebappsFolder.exists()) - return null; - - File webapps = new File(mWebappsFolder, "webapps.json"); - if (!webapps.exists()) - return null; - - // Parse the contents into a string - String webappsJson = new String(); - try { - BufferedReader in = new BufferedReader(new FileReader(webapps)); - String line = new String(); - - while ((line = in.readLine()) != null) { - webappsJson += line; - } - } catch (IOException e) { } - - if (webappsJson.length() == 0) + if (TextUtils.isEmpty(webappsJson)) return null; mWebappsList = new ArrayList<HashMap<String, String>>(); try { JSONObject webApps = (JSONObject) new JSONTokener(webappsJson).nextValue(); @SuppressWarnings("rawtypes") Iterator appKeys = webApps.keys();
--- a/mobile/android/base/Makefile.in +++ b/mobile/android/base/Makefile.in @@ -78,21 +78,21 @@ FENNEC_JAVA_FILES = \ Favicons.java \ FloatUtils.java \ GeckoActionBar.java \ GeckoApp.java \ GeckoAppShell.java \ GeckoAsyncTask.java \ GeckoBatteryManager.java \ GeckoConnectivityReceiver.java \ - GeckoDirProvider.java \ GeckoEvent.java \ GeckoEventListener.java \ GeckoInputConnection.java \ GeckoPreferences.java \ + GeckoProfile.java \ GeckoStateListDrawable.java \ GeckoThread.java \ GlobalHistory.java \ LinkPreference.java \ ProfileMigrator.java \ PromptService.java \ sqlite/ByteBufferInputStream.java \ sqlite/SQLiteBridge.java \
--- a/mobile/android/base/db/BrowserProvider.java.in +++ b/mobile/android/base/db/BrowserProvider.java.in @@ -43,17 +43,17 @@ package @ANDROID_PACKAGE_NAME@.db; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Random; import org.mozilla.gecko.GeckoAppShell; -import org.mozilla.gecko.GeckoDirProvider; +import org.mozilla.gecko.GeckoProfile; import org.mozilla.gecko.R; import org.mozilla.gecko.db.BrowserContract.Bookmarks; import org.mozilla.gecko.db.BrowserContract.CommonColumns; import org.mozilla.gecko.db.BrowserContract.History; import org.mozilla.gecko.db.BrowserContract.Images; import org.mozilla.gecko.db.BrowserContract.Schema; import org.mozilla.gecko.db.BrowserContract.SyncColumns; import org.mozilla.gecko.db.BrowserContract.URLColumns; @@ -649,23 +649,17 @@ public class BrowserProvider extends Con // On Android releases older than 2.3, it's not possible to use // SQLiteOpenHelper with a full path. Fallback to using separate // db files per profile in the app directory. if (Build.VERSION.SDK_INT <= 8) { return "browser-" + profile + ".db"; } - File profileDir = null; - try { - profileDir = GeckoDirProvider.getProfileDir(mContext, profile); - } catch (IOException ex) { - Log.e(LOGTAG, "Error getting profile dir", ex); - } - + 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); @@ -749,17 +743,17 @@ public class BrowserProvider extends Con @Override public boolean onCreate() { debug("Creating BrowserProvider"); GeckoAppShell.getHandler().post(new Runnable() { public void run() { // Kick this off early. It is synchronized so that other callers will wait try { - GeckoDirProvider.getProfileDir(getContext()); + GeckoProfile.get(getContext()).getDir(); } catch (Exception ex) { Log.e(LOGTAG, "Error getting profile dir", ex); } } }); synchronized (this) { mContext = getContext(); mDatabasePerProfile = new HashMap<String, DatabaseHelper>();
--- a/mobile/android/base/db/PasswordsProvider.java.in +++ b/mobile/android/base/db/PasswordsProvider.java.in @@ -9,19 +9,19 @@ import java.io.File; import java.io.IOException; import java.lang.IllegalArgumentException; import java.util.HashMap; import java.util.ArrayList; import java.util.Random; import org.mozilla.gecko.GeckoApp; import org.mozilla.gecko.GeckoAppShell; -import org.mozilla.gecko.GeckoDirProvider; import org.mozilla.gecko.GeckoEvent; import org.mozilla.gecko.GeckoEventListener; +import org.mozilla.gecko.GeckoProfile; import org.mozilla.gecko.db.BrowserContract.CommonColumns; import org.mozilla.gecko.db.DBUtils; import org.mozilla.gecko.db.BrowserContract.Passwords; import org.mozilla.gecko.db.BrowserContract.DeletedPasswords; import org.mozilla.gecko.db.BrowserContract.SyncColumns; import org.mozilla.gecko.db.BrowserContract; import org.mozilla.gecko.sqlite.SQLiteBridge; import org.mozilla.gecko.sqlite.SQLiteBridgeException; @@ -157,23 +157,17 @@ public class PasswordsProvider extends C } Log.d(LOGTAG, "Successfully created database helper for profile: " + profile); return db; } private String getDatabasePath(String profile) { - File profileDir = null; - try { - profileDir = GeckoDirProvider.getProfileDir(mContext, profile); - } catch (IOException ex) { - Log.e(LOGTAG, "Error getting profile dir", ex); - } - + File profileDir = GeckoProfile.get(mContext, profile).getDir(); if (profileDir == null) { Log.d(LOGTAG, "Couldn't find directory for profile: " + profile); return null; } String databasePath = new File(profileDir, DATABASE_NAME).getAbsolutePath(); return databasePath; }