Bug 718760 - Crypto for the java passwords provider. r=blassey,bsmith
authorWes Johnston <wjohnston@mozilla.com>
Thu, 08 Mar 2012 10:25:44 -0800
changeset 91709 2eae96792a75e67abf4e5286f438f2e579bac8cb
parent 91708 f34baebe186578280e2bd964b73cc33559069ec5
child 91710 5031b59e031dc1c4e24851f1453b4d1f4bcd497c
push id783
push userlsblakk@mozilla.com
push dateTue, 24 Apr 2012 17:33:42 +0000
treeherdermozilla-beta@11faed19f136 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersblassey, bsmith
bugs718760
milestone13.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
Bug 718760 - Crypto for the java passwords provider. r=blassey,bsmith
embedding/android/GeckoAppShell.java
mobile/android/base/AndroidManifest.xml.in
mobile/android/base/GeckoApp.java
mobile/android/base/GeckoAppShell.java
mobile/android/base/GeckoMessageReceiver.java
mobile/android/base/GeckoThread.java
mobile/android/base/Makefile.in
mobile/android/base/NSSBridge.java
mobile/android/base/db/BrowserContract.java.in
mobile/android/base/db/DBUtils.java
mobile/android/base/db/FormHistoryProvider.java.in
mobile/android/base/db/GeckoProvider.java.in
mobile/android/base/db/PasswordsProvider.java.in
mobile/android/base/sqlite/MatrixBlobCursor.java
mozglue/android/APKOpen.cpp
mozglue/android/APKOpen.h
mozglue/android/Makefile.in
mozglue/android/NSSBridge.cpp
mozglue/android/NSSBridge.h
mozglue/android/SQLiteBridge.cpp
--- a/embedding/android/GeckoAppShell.java
+++ b/embedding/android/GeckoAppShell.java
@@ -110,16 +110,17 @@ public class GeckoAppShell
     public static native void setSurfaceView(GeckoSurfaceView sv);
     public static native void putenv(String map);
     public static native void onResume();
     public static native void onLowMemory();
     public static native void callObserver(String observerKey, String topic, String data);
     public static native void removeObserver(String observerKey);
     public static native void loadGeckoLibsNative(String apkName);
     public static native void loadSQLiteLibsNative(String apkName, boolean shouldExtract);
+    public static native void loadNSSLibsNative(String apkName, boolean shouldExtract);
     public static native void onChangeNetworkLinkStatus(String status);
     public static native void reportJavaCrash(String stack);
 
     public static native void processNextNativeEvent();
 
     public static native void notifyBatteryChange(double aLevel, boolean aCharging, double aRemainingTime);
 
     public static native void notifySmsReceived(String aSender, String aBody, long aTimestamp);
@@ -393,16 +394,17 @@ public class GeckoAppShell
                 while (cacheFiles.hasNext()) {
                     File libFile = (File)cacheFiles.next();
                     if (libFile.getName().endsWith(".so"))
                         libFile.delete();
                 }
             }
         }
         loadSQLiteLibsNative(apkName, extractLibs);
+        loadNSSLibsNative(apkName, extractLibs);
         loadGeckoLibsNative(apkName);
     }
 
     private static void putLocaleEnv() {
         GeckoAppShell.putenv("LANG=" + Locale.getDefault().toString());
         NumberFormat nf = NumberFormat.getInstance();
         if (nf instanceof DecimalFormat) {
             DecimalFormat df = (DecimalFormat)nf;
--- a/mobile/android/base/AndroidManifest.xml.in
+++ b/mobile/android/base/AndroidManifest.xml.in
@@ -18,16 +18,17 @@
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
     <uses-permission android:name="android.permission.INTERNET"/>
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
     <uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION"/>
     <uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT"/>
 
     <uses-permission android:name="android.permission.WAKE_LOCK"/>
     <uses-permission android:name="android.permission.VIBRATE"/>
+    <uses-permission android:name="@ANDROID_PACKAGE_NAME@.permissions.PASSWORD_PROVIDER"/>
 
 #ifdef MOZ_WEBSMS_BACKEND
     <!-- WebSMS -->
     <uses-permission android:name="android.permission.SEND_SMS"/>
     <uses-permission android:name="android.permission.RECEIVE_SMS"/>
     <uses-permission android:name="android.permission.WRITE_SMS"/>
     <uses-permission android:name="android.permission.READ_SMS"/>
 #endif
@@ -111,16 +112,23 @@
 
         <receiver android:name="NotificationHandler">
             <intent-filter>
                 <action android:name="org.mozilla.gecko.ACTION_ALERT_CLICK" />
                 <action android:name="org.mozilla.gecko.ACTION_ALERT_CLEAR" />
             </intent-filter>
         </receiver>
 
+        <receiver android:name="org.mozilla.gecko.GeckoMessageReceiver"
+                  android:permission="@ANDROID_PACKAGE_NAME@.permissions.PASSWORD_PROVIDER">
+            <intent-filter>
+                  <action android:name="org.mozilla.gecko.INIT_PW"></action>
+            </intent-filter>
+        </receiver>
+
         <activity android:name="Restarter"
                   android:process="@ANDROID_PACKAGE_NAME@Restarter"
                   android:theme="@style/Gecko"
                   android:excludeFromRecents="true">
           <intent-filter>
             <action android:name="org.mozilla.gecko.restart"/>
             <action android:name="org.mozilla.gecko.restart_update"/>
           </intent-filter>
@@ -170,30 +178,33 @@
                   android:excludeFromRecents="true"/>
 
         <provider android:name="@ANDROID_PACKAGE_NAME@.db.BrowserProvider"
                   android:authorities="@ANDROID_PACKAGE_NAME@.db.browser"
                   android:permission="@ANDROID_PACKAGE_NAME@.permissions.BROWSER_PROVIDER"/>
 
         <provider android:name="@ANDROID_PACKAGE_NAME@.db.PasswordsProvider"
                   android:authorities="@ANDROID_PACKAGE_NAME@.db.passwords"
-                  android:permission="@ANDROID_PACKAGE_NAME@.permissions.BROWSER_PROVIDER"
-                  android:protectionLevel="signature"/>
+                  android:permission="@ANDROID_PACKAGE_NAME@.permissions.PASSWORD_PROVIDER"
+                  android:process="org.mozilla.gecko.PasswordsProvider"/>
 
         <provider android:name="@ANDROID_PACKAGE_NAME@.db.FormHistoryProvider"
                   android:authorities="@ANDROID_PACKAGE_NAME@.db.formhistory"
                   android:permission="@ANDROID_PACKAGE_NAME@.permissions.FORMHISTORY_PROVIDER"
                   android:protectionLevel="signature"/>
 
         <provider android:name="@ANDROID_PACKAGE_NAME@.db.TabsProvider"
                   android:authorities="@ANDROID_PACKAGE_NAME@.db.tabs"
                   android:permission="@ANDROID_PACKAGE_NAME@.permissions.BROWSER_PROVIDER"/>
 
 #include ../sync/manifests/SyncAndroidManifest_services.xml.in
     </application>
 
     <permission android:name="@ANDROID_PACKAGE_NAME@.permissions.BROWSER_PROVIDER"
                 android:protectionLevel="signature"/>
 
+    <permission android:name="@ANDROID_PACKAGE_NAME@.permissions.PASSWORD_PROVIDER"
+                android:protectionLevel="signature"/>
+
     <permission android:name="@ANDROID_PACKAGE_NAME@.permissions.FORMHISTORY_PROVIDER"
                 android:protectionLevel="signature"/>
 
 </manifest> 
--- a/mobile/android/base/GeckoApp.java
+++ b/mobile/android/base/GeckoApp.java
@@ -104,16 +104,17 @@ abstract public class GeckoApp
 
     public static final String ACTION_ALERT_CLICK   = "org.mozilla.gecko.ACTION_ALERT_CLICK";
     public static final String ACTION_ALERT_CLEAR   = "org.mozilla.gecko.ACTION_ALERT_CLEAR";
     public static final String ACTION_WEBAPP        = "org.mozilla.gecko.WEBAPP";
     public static final String ACTION_DEBUG         = "org.mozilla.gecko.DEBUG";
     public static final String ACTION_BOOKMARK      = "org.mozilla.gecko.BOOKMARK";
     public static final String ACTION_LOAD          = "org.mozilla.gecko.LOAD";
     public static final String ACTION_UPDATE        = "org.mozilla.gecko.UPDATE";
+    public static final String ACTION_INIT_PW       = "org.mozilla.gecko.INIT_PW";
     public static final String SAVED_STATE_URI      = "uri";
     public static final String SAVED_STATE_TITLE    = "title";
     public static final String SAVED_STATE_VIEWPORT = "viewport";
     public static final String SAVED_STATE_SCREEN   = "screen";
     public static final String SAVED_STATE_SESSION  = "session";
 
     StartupMode mStartupMode = null;
     private LinearLayout mMainLayout;
--- a/mobile/android/base/GeckoAppShell.java
+++ b/mobile/android/base/GeckoAppShell.java
@@ -107,16 +107,17 @@ public class GeckoAppShell
     static public final int WPL_STATE_IS_DOCUMENT = 0x00020000;
     static public final int WPL_STATE_IS_NETWORK = 0x00040000;
 
     static private File sCacheFile = null;
     static private int sFreeSpace = -1;
     static File sHomeDir = null;
     static private int sDensityDpi = 0;
     private static Boolean sSQLiteLibsLoaded = false;
+    private static Boolean sNSSLibsLoaded = false;
     private static Boolean sLibsSetup = false;
     private static File sGREDir = null;
 
     private static HashMap<String, ArrayList<GeckoEventListener>> mEventListeners;
 
     /* Is the value in sVibrationEndTime valid? */
     private static boolean sVibrationMaybePlaying = false;
 
@@ -136,16 +137,17 @@ public class GeckoAppShell
     public static native void setSoftwareLayerClient(GeckoSoftwareLayerClient client);
     public static native void putenv(String map);
     public static native void onResume();
     public static native void onLowMemory();
     public static native void callObserver(String observerKey, String topic, String data);
     public static native void removeObserver(String observerKey);
     public static native void loadGeckoLibsNative(String apkName);
     public static native void loadSQLiteLibsNative(String apkName, boolean shouldExtract);
+    public static native void loadNSSLibsNative(String apkName, boolean shouldExtract);
     public static native void onChangeNetworkLinkStatus(String status);
 
     public static void registerGlobalExceptionHandler() {
         Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
             public void uncaughtException(Thread thread, Throwable e) {
                 Log.e(LOGTAG, ">>> REPORTING UNCAUGHT EXCEPTION FROM THREAD "
                               + thread.getId() + " (\"" + thread.getName() + "\")", e);
                 reportJavaCrash(getStackTraceString(e));
@@ -366,16 +368,29 @@ public class GeckoAppShell
             loadMozGlue();
             // the extract libs parameter is being removed in bug 732069
             loadLibsSetup(context);
             loadSQLiteLibsNative(apkName, false);
             sSQLiteLibsLoaded = true;
         }
     }
 
+    public static void loadNSSLibs(Context context, String apkName) {
+        if (sNSSLibsLoaded)
+            return;
+        synchronized(sNSSLibsLoaded) {
+            if (sNSSLibsLoaded)
+                return;
+            loadMozGlue();
+            loadLibsSetup(context);
+            loadNSSLibsNative(apkName, false);
+            sNSSLibsLoaded = true;
+        }
+    }
+
     public static void loadMozGlue() {
         System.loadLibrary("mozglue");
     }
 
     public static void loadGeckoLibs(String apkName) {
         loadLibsSetup(GeckoApp.mAppContext);
         loadGeckoLibsNative(apkName);
     }
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/GeckoMessageReceiver.java
@@ -0,0 +1,20 @@
+/* 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;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+public class GeckoMessageReceiver extends BroadcastReceiver {
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        final String action = intent.getAction();
+        if (GeckoApp.ACTION_INIT_PW.equals(action)) {
+            GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Passwords:Init", null));
+        }
+    }
+}
--- a/mobile/android/base/GeckoThread.java
+++ b/mobile/android/base/GeckoThread.java
@@ -85,16 +85,17 @@ public class GeckoThread extends Thread 
 
         // At some point while loading the gecko libs our default locale gets set
         // so just save it to locale here and reset it as default after the join
         Locale locale = Locale.getDefault();
 
         String resourcePath = app.getApplication().getPackageResourcePath();
         GeckoAppShell.setupGeckoEnvironment(app);
         GeckoAppShell.loadSQLiteLibs(app, resourcePath);
+        GeckoAppShell.loadNSSLibs(app, resourcePath);
         GeckoAppShell.loadGeckoLibs(resourcePath);
 
         Locale.setDefault(locale);
         Resources res = app.getBaseContext().getResources();
         Configuration config = res.getConfiguration();
         config.locale = locale;
         res.updateConfiguration(config, res.getDisplayMetrics());
 
--- a/mobile/android/base/Makefile.in
+++ b/mobile/android/base/Makefile.in
@@ -83,23 +83,25 @@ FENNEC_JAVA_FILES = \
   GeckoAsyncTask.java \
   GeckoBatteryManager.java \
   GeckoBackgroundThread.java \
   GeckoConnectivityReceiver.java \
   GeckoEvent.java \
   GeckoEventListener.java \
   GeckoEventResponder.java \
   GeckoInputConnection.java \
+	GeckoMessageReceiver.java \
   GeckoPreferences.java \
   GeckoProfile.java \
   GeckoStateListDrawable.java \
   GeckoThread.java \
   GlobalHistory.java \
   LinkPreference.java \
   LinkTextView.java \
+	NSSBridge.java \
   ProfileMigrator.java \
   PromptService.java \
   sqlite/ByteBufferInputStream.java \
   sqlite/MatrixBlobCursor.java \
   sqlite/SQLiteBridge.java \
   sqlite/SQLiteBridgeException.java \
   RemoteTabs.java \
   SetupScreen.java \
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/NSSBridge.java
@@ -0,0 +1,63 @@
+/* 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;
+
+import android.app.Activity;
+import android.util.Log;
+import android.content.Context;
+import java.lang.String;
+
+public class NSSBridge {
+    private static final String LOGTAG = "NSSBridge";
+
+    private static native String nativeEncrypt(String aDb, String aValue);
+    private static native String nativeDecrypt(String aDb, String aValue);
+
+    static public String encrypt(Context context, String aValue) {
+        String resourcePath = context.getPackageResourcePath();
+        GeckoAppShell.loadNSSLibs(context, resourcePath);
+
+        String res = "";
+        try {
+            String path = GeckoProfile.get(context).getDir().toString();
+            res = nativeEncrypt(path, aValue);
+        } catch(Exception ex) { }
+        return res;
+    }
+
+    static public String encrypt(Context context, String profilePath, String aValue) {
+        String resourcePath = context.getPackageResourcePath();
+        GeckoAppShell.loadNSSLibs(context, resourcePath);
+
+        String res = "";
+        try {
+            res = nativeEncrypt(profilePath, aValue);
+        } catch(Exception ex) { }
+        return res;
+    }
+
+    static public String decrypt(Context context, String aValue) {
+        String resourcePath = context.getPackageResourcePath();
+        GeckoAppShell.loadNSSLibs(context, resourcePath);
+
+        String res = "";
+        try {
+            String path = GeckoProfile.get(context).getDir().toString();
+            res = nativeDecrypt(path, aValue);
+        } catch(Exception ex) { }
+        return res;
+    }
+
+    static public String decrypt(Context context, String profilePath, String aValue) {
+        String resourcePath = context.getPackageResourcePath();
+        GeckoAppShell.loadNSSLibs(context, resourcePath);
+
+        String res = "";
+        try {
+            res = nativeDecrypt(profilePath, aValue);
+        } catch(Exception ex) { }
+        return res;
+    }
+}
--- a/mobile/android/base/db/BrowserContract.java.in
+++ b/mobile/android/base/db/BrowserContract.java.in
@@ -40,25 +40,20 @@ package org.mozilla.gecko.db;
 
 import android.net.Uri;
 
 public class BrowserContract {
     public static final String AUTHORITY = "@ANDROID_PACKAGE_NAME@.db.browser";
     public static final Uri AUTHORITY_URI = Uri.parse("content://" + AUTHORITY);
 
     public static final String PASSWORDS_AUTHORITY = "@ANDROID_PACKAGE_NAME@.db.passwords";
-    public static final String DELETED_PASSWORDS_AUTHORITY = "@ANDROID_PACKAGE_NAME@.db.deleted-passwords";
-
     public static final Uri PASSWORDS_AUTHORITY_URI = Uri.parse("content://" + PASSWORDS_AUTHORITY);
-    public static final Uri DELETED_PASSWORDS_AUTHORITY_URI = Uri.parse("content://" + DELETED_PASSWORDS_AUTHORITY);
 
     public static final String FORM_HISTORY_AUTHORITY = "@ANDROID_PACKAGE_NAME@.db.formhistory";
-    public static final String DELETED_FORM_HISTORY_AUTHORITY = "@ANDROID_PACKAGE_NAME@.db.deleted-formhistory";
     public static final Uri FORM_HISTORY_AUTHORITY_URI = Uri.parse("content://" + FORM_HISTORY_AUTHORITY);
-    public static final Uri DELETED_FORM_HISTORY_AUTHORITY_URI = Uri.parse("content://" + DELETED_FORM_HISTORY_AUTHORITY);
     
     public static final String TABS_AUTHORITY = "@ANDROID_PACKAGE_NAME@.db.tabs";
     public static final Uri TABS_AUTHORITY_URI = Uri.parse("content://" + TABS_AUTHORITY);
 
     public static final String DEFAULT_PROFILE = "default";
     public static final String PARAM_PROFILE = "profile";
     public static final String PARAM_PROFILE_PATH = "profilePath";
     public static final String PARAM_LIMIT = "limit";
@@ -163,17 +158,17 @@ public class BrowserContract {
         public static final String TIME_PASSWORD_CHANGED = "timePasswordChanged";
         public static final String TIMES_USED = "timesUsed";
         public static final String GUID = "guid";
     }
 
     public static final class DeletedPasswords implements DeletedColumns {
         private DeletedPasswords() {}
         public static final String CONTENT_TYPE = "vnd.android.cursor.dir/deleted-passwords";
-        public static final Uri CONTENT_URI = Uri.withAppendedPath(DELETED_PASSWORDS_AUTHORITY_URI, "deleted-formhistory");
+        public static final Uri CONTENT_URI = Uri.withAppendedPath(PASSWORDS_AUTHORITY_URI, "deleted-passwords");
     }
 
     public static final class FormHistory {
         private FormHistory() {}
         public static final Uri CONTENT_URI = Uri.withAppendedPath(FORM_HISTORY_AUTHORITY_URI, "formhistory");
         public static final String CONTENT_TYPE = "vnd.android.cursor.dir/formhistory";
 
         public static final String ID = "id";
@@ -182,17 +177,17 @@ public class BrowserContract {
         public static final String TIMES_USED = "timesUsed";
         public static final String FIRST_USED = "firstUsed";
         public static final String LAST_USED = "lastUsed";
         public static final String GUID = "guid";
     }
 
     public static final class DeletedFormHistory implements DeletedColumns {
         private DeletedFormHistory() {}
-        public static final Uri CONTENT_URI = Uri.withAppendedPath(DELETED_FORM_HISTORY_AUTHORITY_URI, "deleted-formhistory");
+        public static final Uri CONTENT_URI = Uri.withAppendedPath(FORM_HISTORY_AUTHORITY_URI, "deleted-formhistory");
         public static final String CONTENT_TYPE = "vnd.android.cursor.dir/deleted-formhistory";
     }
 
     public static final class Tabs implements CommonColumns {
         private Tabs() {}
         public static final Uri CONTENT_URI = Uri.withAppendedPath(TABS_AUTHORITY_URI, "tabs");
         public static final String CONTENT_TYPE = "vnd.android.cursor.dir/tab";
         public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/tab";
--- a/mobile/android/base/db/DBUtils.java
+++ b/mobile/android/base/db/DBUtils.java
@@ -54,13 +54,13 @@ public class DBUtils {
     public static void replaceKey(ContentValues aValues, String aOriginalKey,
                                   String aNewKey, String aDefault) {
         String value = aDefault;
         if (aOriginalKey != null && aValues.containsKey(aOriginalKey)) {
             value = aValues.get(aOriginalKey).toString();
             aValues.remove(aOriginalKey);
         }
 
-        if (!aValues.containsKey(aOriginalKey)) {
+        if (!aValues.containsKey(aNewKey)) {
             aValues.put(aNewKey, value);
         }
     }
 }
--- a/mobile/android/base/db/FormHistoryProvider.java.in
+++ b/mobile/android/base/db/FormHistoryProvider.java.in
@@ -28,19 +28,16 @@ import org.mozilla.gecko.sqlite.SQLiteBr
 import org.mozilla.gecko.sync.Utils;
 
 import android.content.ContentProvider;
 import android.content.ContentUris;
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.UriMatcher;
 import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteOpenHelper;
-import android.database.sqlite.SQLiteQueryBuilder;
 import android.net.Uri;
 import android.os.Build;
 import android.text.TextUtils;
 import android.util.Log;
 
 public class FormHistoryProvider extends GeckoProvider {
     static final String TABLE_FORM_HISTORY = "moz_formhistory";
     static final String TABLE_DELETED_FORM_HISTORY = "moz_deleted_formhistory";
@@ -55,17 +52,17 @@ public class FormHistoryProvider extends
 
     // This should be kept in sync with the db version in toolkit/components/satchel/nsFormHistory.js
     private static int DB_VERSION = 4;
     private static String DB_FILENAME = "formhistory.sqlite";
 
     static {
         URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
         URI_MATCHER.addURI(BrowserContract.FORM_HISTORY_AUTHORITY, "formhistory", FORM_HISTORY);
-        URI_MATCHER.addURI(BrowserContract.DELETED_FORM_HISTORY_AUTHORITY, "deleted-formhistory", DELETED_FORM_HISTORY);
+        URI_MATCHER.addURI(BrowserContract.FORM_HISTORY_AUTHORITY, "deleted-formhistory", DELETED_FORM_HISTORY);
         FORM_HISTORY_PROJECTION_MAP = new HashMap<String, String>();
         DELETED_FORM_HISTORY_PROJECTION_MAP = new HashMap<String, String>();
     }
 
     @Override
     public boolean onCreate() {
         setLogTag("FormHistoryProvider");
         setDBName(DB_FILENAME);
@@ -141,9 +138,15 @@ public class FormHistoryProvider extends
             default:
                 throw new UnsupportedOperationException("Unknown insert URI " + uri);
         }
     }
 
     public void initGecko() {
         GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("FormHistory:Init", null));
     }
+
+    public void onPreInsert(ContentValues values, Uri uri) { }
+
+    public void onPreUpdate(ContentValues values, Uri uri) { }
+
+    public void onPostQuery(Cursor cursor, Uri uri) { }
 }
--- a/mobile/android/base/db/GeckoProvider.java.in
+++ b/mobile/android/base/db/GeckoProvider.java.in
@@ -85,16 +85,17 @@ public abstract class GeckoProvider exte
 
     private SQLiteBridge getDB(Context context, final String databasePath) {
         SQLiteBridge bridge = null;
 
         boolean dbNeedsSetup = true;
         try {
             String resourcePath = context.getPackageResourcePath();
             GeckoAppShell.loadSQLiteLibs(context, resourcePath);
+            GeckoAppShell.loadNSSLibs(context, resourcePath);
             bridge = new SQLiteBridge(databasePath);
             int version = bridge.getVersion();
             Log.i(mLogTag, version + " == " + mDBVersion);
             dbNeedsSetup = version != mDBVersion;
         } catch(SQLiteBridgeException ex) {
             // this will throw if the database can't be found
             // we should attempt to set it up if Gecko is running
             dbNeedsSetup = true;
@@ -216,16 +217,18 @@ public abstract class GeckoProvider exte
         // If we can not get a SQLiteBridge instance, its likely that the database
         // has not been set up and Gecko is not running. We return null and expect
         // callers to try again later
         if (db == null)
             return null;
 
         setupDefaults(uri, values);
 
+        onPreInsert(values, uri);
+
         try {
             id = db.insert(getTable(uri), null, values);
         } catch(SQLiteBridgeException ex) {
             Log.e(mLogTag, "Error inserting in db", ex);
         }
 
         return ContentUris.withAppendedId(uri, id);
     }
@@ -237,16 +240,18 @@ public abstract class GeckoProvider exte
         final SQLiteBridge db = getDatabase(uri);
 
         // If we can not get a SQLiteBridge instance, its likely that the database
         // has not been set up and Gecko is not running. We return null and expect
         // callers to try again later
         if (db == null)
             return updated;
 
+        onPreUpdate(values, uri);
+
         try {
             updated = db.update(getTable(uri), values, selection, selectionArgs);
         } catch(SQLiteBridgeException ex) {
             Log.e(mLogTag, "Error updating table", ex);
         }
 
         return updated;
     }
@@ -262,23 +267,30 @@ public abstract class GeckoProvider exte
         // callers to try again later
         if (db == null)
             return cursor;
 
         sortOrder = getSortOrder(uri, sortOrder);
 
         try {
             cursor = db.query(getTable(uri), projection, selection, selectionArgs, null, null, sortOrder, null);
+            onPostQuery(cursor, uri);
         } catch (SQLiteBridgeException ex) {
             Log.e(mLogTag, "Error querying database", ex);
         }
 
         return cursor;
     }
 
     public abstract String getTable(Uri uri);
 
     public abstract String getSortOrder(Uri uri, String aRequested);
 
     public abstract void setupDefaults(Uri uri, ContentValues values);
 
     public abstract void initGecko();
+
+    public abstract void onPreInsert(ContentValues values, Uri uri);
+
+    public abstract void onPreUpdate(ContentValues values, Uri uri);
+
+    public abstract void onPostQuery(Cursor cursor, Uri uri);
 }
--- a/mobile/android/base/db/PasswordsProvider.java.in
+++ b/mobile/android/base/db/PasswordsProvider.java.in
@@ -12,35 +12,34 @@ 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.GeckoEvent;
 import org.mozilla.gecko.GeckoEventListener;
 import org.mozilla.gecko.GeckoProfile;
-import org.mozilla.gecko.db.BrowserContract.CommonColumns;
+import org.mozilla.gecko.NSSBridge;
 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.MatrixBlobCursor;
 import org.mozilla.gecko.sqlite.SQLiteBridge;
 import org.mozilla.gecko.sqlite.SQLiteBridgeException;
 import org.mozilla.gecko.sync.Utils;
 
 import android.content.ContentProvider;
 import android.content.ContentUris;
 import android.content.ContentValues;
 import android.content.Context;
+import android.content.Intent;
 import android.content.UriMatcher;
 import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteOpenHelper;
-import android.database.sqlite.SQLiteQueryBuilder;
 import android.net.Uri;
 import android.os.Build;
 import android.text.TextUtils;
 import android.util.Log;
 
 public class PasswordsProvider extends GeckoProvider {
     static final String TABLE_PASSWORDS = "moz_logins";
     static final String TABLE_DELETED_PASSWORDS = "moz_deleted_logins";
@@ -77,22 +76,23 @@ public class PasswordsProvider extends G
         PASSWORDS_PROJECTION_MAP.put(Passwords.ENCRYPTED_PASSWORD, Passwords.ENCRYPTED_PASSWORD);
         PASSWORDS_PROJECTION_MAP.put(Passwords.GUID, Passwords.GUID);
         PASSWORDS_PROJECTION_MAP.put(Passwords.ENC_TYPE, Passwords.ENC_TYPE);
         PASSWORDS_PROJECTION_MAP.put(Passwords.TIME_CREATED, Passwords.TIME_CREATED);
         PASSWORDS_PROJECTION_MAP.put(Passwords.TIME_LAST_USED, Passwords.TIME_LAST_USED);
         PASSWORDS_PROJECTION_MAP.put(Passwords.TIME_PASSWORD_CHANGED, Passwords.TIME_PASSWORD_CHANGED);
         PASSWORDS_PROJECTION_MAP.put(Passwords.TIMES_USED, Passwords.TIMES_USED);
 
-        URI_MATCHER.addURI(BrowserContract.DELETED_PASSWORDS_AUTHORITY, "deleted-passwords", DELETED_PASSWORDS);
+        URI_MATCHER.addURI(BrowserContract.PASSWORDS_AUTHORITY, "deleted-passwords", DELETED_PASSWORDS);
 
         DELETED_PASSWORDS_PROJECTION_MAP = new HashMap<String, String>();
         DELETED_PASSWORDS_PROJECTION_MAP.put(DeletedPasswords.ID, DeletedPasswords.ID);
         DELETED_PASSWORDS_PROJECTION_MAP.put(DeletedPasswords.GUID, DeletedPasswords.GUID);
         DELETED_PASSWORDS_PROJECTION_MAP.put(DeletedPasswords.TIME_DELETED, DeletedPasswords.TIME_DELETED);
+        System.loadLibrary("mozglue");
     }
 
     @Override
     public boolean onCreate() {
         setLogTag("GeckoPasswordsProvider");
         setDBName(DB_FILENAME);
         setDBVersion(DB_VERSION);
         return super.onCreate();
@@ -184,10 +184,83 @@ public class PasswordsProvider extends G
             default:
                 throw new UnsupportedOperationException("Unknown URI " + uri);
         }
     }
 
     @Override
     public void initGecko() {
         GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Passwords:Init", null));
+        Intent initIntent = new Intent(GeckoApp.ACTION_INIT_PW);
+        mContext.sendBroadcast(initIntent);
+    }
+
+    private String doCrypto(String initialValue, Uri uri, Boolean encrypt) {
+        String profilePath = null;
+        if (uri != null) {
+            profilePath = uri.getQueryParameter(BrowserContract.PARAM_PROFILE_PATH);
+        }
+
+        String result = "";
+        if (encrypt) {
+          if (profilePath != null) result = NSSBridge.encrypt(mContext, profilePath, initialValue);
+          else result = NSSBridge.encrypt(mContext, initialValue);
+        } else {
+          if (profilePath != null) result = NSSBridge.decrypt(mContext, profilePath, initialValue);
+          else result = NSSBridge.decrypt(mContext, initialValue);            
+        }
+        return result;
+    }
+
+    public void onPreInsert(ContentValues values, Uri uri) {
+        if (values.containsKey(Passwords.ENCRYPTED_PASSWORD)) {
+            String res = doCrypto(values.getAsString(Passwords.ENCRYPTED_PASSWORD), uri, true);
+            values.put(Passwords.ENCRYPTED_PASSWORD, res);
+        }
+
+        if (values.containsKey(Passwords.ENCRYPTED_USERNAME)) {
+            String res = doCrypto(values.getAsString(Passwords.ENCRYPTED_USERNAME), uri, true);
+            values.put(Passwords.ENCRYPTED_USERNAME, res);
+        }
+    }
+ 
+    public void onPreUpdate(ContentValues values, Uri uri) {
+        if (values.containsKey(Passwords.ENCRYPTED_PASSWORD)) {
+            String res = doCrypto(values.getAsString(Passwords.ENCRYPTED_PASSWORD), uri, true);
+            values.put(Passwords.ENCRYPTED_PASSWORD, res);
+        }
+
+        if (values.containsKey(Passwords.ENCRYPTED_USERNAME)) {
+            String res = doCrypto(values.getAsString(Passwords.ENCRYPTED_USERNAME), uri, true);
+            values.put(Passwords.ENCRYPTED_USERNAME, res);
+        }
+    }
+
+    public void onPostQuery(Cursor cursor, Uri uri) {
+        int passwordIndex = -1;
+        int usernameIndex = -1;
+        String profilePath = null;
+
+        try {
+            passwordIndex = cursor.getColumnIndexOrThrow(Passwords.ENCRYPTED_PASSWORD);
+        } catch(Exception ex) { }
+        try {
+            usernameIndex = cursor.getColumnIndexOrThrow(Passwords.ENCRYPTED_USERNAME);
+        } catch(Exception ex) { }
+
+        if (passwordIndex > -1 || usernameIndex > -1) {
+            MatrixBlobCursor m = (MatrixBlobCursor)cursor;
+            if (cursor.moveToFirst()) {
+                do {
+                    if (passwordIndex > -1) {
+                        String decrypted = doCrypto(cursor.getString(passwordIndex), uri, false);;
+                        m.set(passwordIndex, decrypted);
+                    }
+
+                    if (usernameIndex > -1) {
+                        String decrypted = doCrypto(cursor.getString(usernameIndex), uri, false);
+                        m.set(usernameIndex, decrypted);
+                    }
+                } while(cursor.moveToNext());
+            }
+        }
     }
 }
--- a/mobile/android/base/sqlite/MatrixBlobCursor.java
+++ b/mobile/android/base/sqlite/MatrixBlobCursor.java
@@ -223,18 +223,31 @@ public class MatrixBlobCursor extends Ab
                         "No more columns left.");
             }
 
             data[index++] = columnValue;
             return this;
         }
     }
 
+    public void set(int column, Object value) {
+        if (column < 0 || column >= columnCount) {
+            throw new CursorIndexOutOfBoundsException("Requested column: "
+                    + column + ", # of columns: " +  columnCount);
+        }
+        if (mPos < 0) {
+            throw new CursorIndexOutOfBoundsException("Before first row.");
+        }
+        if (mPos >= rowCount) {
+            throw new CursorIndexOutOfBoundsException("After last row.");
+        }
+        data[mPos * columnCount + column] = value;
+    }
+
     // AbstractCursor implementation.
-
     @Override
     public int getCount() {
         return rowCount;
     }
 
     @Override
     public String[] getColumnNames() {
         return columnNames;
--- a/mozglue/android/APKOpen.cpp
+++ b/mozglue/android/APKOpen.cpp
@@ -61,26 +61,29 @@
 #endif
 #include "dlfcn.h"
 #include "APKOpen.h"
 #include <sys/time.h>
 #include <sys/resource.h>
 #include "Zip.h"
 #include "sqlite3.h"
 #include "SQLiteBridge.h"
+#include "NSSBridge.h"
 #ifndef MOZ_OLD_LINKER
 #include "ElfLoader.h"
 #endif
 #include "application.ini.h"
 
 /* Android headers don't define RUSAGE_THREAD */
 #ifndef RUSAGE_THREAD
 #define RUSAGE_THREAD 1
 #endif
 
+typedef int mozglueresult;
+
 enum StartupEvent {
 #define mozilla_StartupTimeline_Event(ev, z) ev,
 #include "StartupTimeline.h"
 #undef mozilla_StartupTimeline_Event
 };
 
 using namespace mozilla;
 
@@ -94,16 +97,33 @@ void StartupTimeline_Record(StartupEvent
 static struct mapping_info * lib_mapping = NULL;
 
 NS_EXPORT const struct mapping_info *
 getLibraryMapping()
 {
   return lib_mapping;
 }
 
+void
+JNI_Throw(JNIEnv* jenv, const char* classname, const char* msg)
+{
+    __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Throw\n");
+    jclass cls = jenv->FindClass(classname);
+    if (cls == NULL) {
+        __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Couldn't find exception class (or exception pending) %s\n", classname);
+        exit(FAILURE);
+    }
+    int rc = jenv->ThrowNew(cls, msg);
+    if (rc < 0) {
+        __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Error throwing exception %s\n", msg);
+        exit(FAILURE);
+    }
+    jenv->DeleteLocalRef(cls);
+}
+
 #ifdef MOZ_OLD_LINKER
 static int
 createAshmem(size_t bytes, const char *name)
 {
   int fd = open("/" ASHMEM_NAME_DEF, O_RDWR, 0600);
   if (fd < 0)
     return -1;
 
@@ -308,16 +328,21 @@ SHELL_WRAPPER3(notifySmsDeleted, jboolea
 SHELL_WRAPPER3(notifySmsDeleteFailed, jint, jint, jlong)
 SHELL_WRAPPER2(notifyNoMessageInList, jint, jlong)
 SHELL_WRAPPER8(notifyListCreated, jint, jint, jstring, jstring, jstring, jlong, jint, jlong)
 SHELL_WRAPPER7(notifyGotNextMessage, jint, jstring, jstring, jstring, jlong, jint, jlong)
 SHELL_WRAPPER3(notifyReadingMessageListFailed, jint, jint, jlong)
 
 static void * xul_handle = NULL;
 static void * sqlite_handle = NULL;
+static void * nss_handle = NULL;
+static void * nspr_handle = NULL;
+static void * plc_handle = NULL;
+static bool simple_linker_initialized = false;
+
 #ifdef MOZ_OLD_LINKER
 static time_t apk_mtime = 0;
 #ifdef DEBUG
 extern "C" int extractLibs = 1;
 #else
 extern "C" int extractLibs = 0;
 #endif
 
@@ -636,17 +661,17 @@ report_mapping(char *name, void *base, u
   if (entry)
     info->file_id = strndup(entry + strlen(name) + 1, 32);
 }
 
 #ifdef MOZ_OLD_LINKER
 extern "C" void simple_linker_init(void);
 #endif
 
-static void
+static mozglueresult
 loadGeckoLibs(const char *apkName)
 {
   chdir(getenv("GRE_HOME"));
 
 #ifdef MOZ_OLD_LINKER
   struct stat status;
   if (!stat(apkName, &status))
     apk_mtime = status.st_mtime;
@@ -668,38 +693,30 @@ loadGeckoLibs(const char *apkName)
   sprintf(file, "%s!/libxpcom.so", apkName);
   __wrap_dlopen(file, RTLD_GLOBAL | RTLD_LAZY);
   // libxul.so is pulled from libxpcom.so, so we don't need to give the full path
   xul_handle = __wrap_dlopen("libxul.so", RTLD_GLOBAL | RTLD_LAZY);
   delete[] file;
 #else
 #define MOZLOAD(name) mozload("lib" name ".so", zip)
   MOZLOAD("mozalloc");
-  MOZLOAD("nspr4");
-  MOZLOAD("plc4");
-  MOZLOAD("plds4");
-  MOZLOAD("nssutil3");
-  MOZLOAD("nss3");
-  MOZLOAD("ssl3");
-  MOZLOAD("smime3");
   xul_handle = MOZLOAD("xul");
   MOZLOAD("xpcom");
-  MOZLOAD("nssckbi");
-  MOZLOAD("freebl3");
-  MOZLOAD("softokn3");
 #undef MOZLOAD
 #endif
 
 #ifdef MOZ_CRASHREPORTER
   free(file_ids);
   file_ids = NULL;
 #endif
 
-  if (!xul_handle)
+  if (!xul_handle) {
     __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Couldn't get a handle to libxul!");
+    return FAILURE;
+  }
 
 #define GETFUNC(name) f_ ## name = (name ## _t) __wrap_dlsym(xul_handle, "Java_org_mozilla_gecko_GeckoAppShell_" #name)
   GETFUNC(nativeInit);
   GETFUNC(notifyGeckoOfEvent);
   GETFUNC(processNextNativeEvent);
   GETFUNC(setSurfaceView);
   GETFUNC(setSoftwareLayerClient);
   GETFUNC(onResume);
@@ -733,32 +750,38 @@ loadGeckoLibs(const char *apkName)
   __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Loaded libs in %ldms total, %ldms user, %ldms system, %ld faults",
                       (t1.tv_sec - t0.tv_sec)*1000 + (t1.tv_usec - t0.tv_usec)/1000, 
                       (usage2.ru_utime.tv_sec - usage1.ru_utime.tv_sec)*1000 + (usage2.ru_utime.tv_usec - usage1.ru_utime.tv_usec)/1000,
                       (usage2.ru_stime.tv_sec - usage1.ru_stime.tv_sec)*1000 + (usage2.ru_stime.tv_usec - usage1.ru_stime.tv_usec)/1000,
                       usage2.ru_majflt-usage1.ru_majflt);
 
   StartupTimeline_Record(LINKER_INITIALIZED, &t0);
   StartupTimeline_Record(LIBRARIES_LOADED, &t1);
+  return SUCCESS;
 }
 
-static void loadSQLiteLibs(const char *apkName)
+static int loadSQLiteLibs(const char *apkName)
 {
   chdir(getenv("GRE_HOME"));
 
 #ifdef MOZ_OLD_LINKER
-  simple_linker_init();
+  if (!simple_linker_initialized) {
+    simple_linker_init();
+    simple_linker_initialized = true;
+  }
 
   struct stat status;
   if (!stat(apkName, &status))
     apk_mtime = status.st_mtime;
 #endif
 
   RefPtr<Zip> zip = new Zip(apkName);
-  lib_mapping = (struct mapping_info *)calloc(MAX_MAPPING_INFO, sizeof(*lib_mapping));
+  if (!lib_mapping) {
+    lib_mapping = (struct mapping_info *)calloc(MAX_MAPPING_INFO, sizeof(*lib_mapping));
+  }
 
 #ifdef MOZ_CRASHREPORTER
   file_ids = (char *)extractBuf("lib.id", zip);
 #endif
 
 #ifndef MOZ_OLD_LINKER
   char *file = new char[strlen(apkName) + sizeof("!/libmozsqlite3.so")];
   sprintf(file, "%s!/libmozsqlite3.so", apkName);
@@ -770,33 +793,122 @@ static void loadSQLiteLibs(const char *a
 #undef MOZLOAD
 #endif
 
 #ifdef MOZ_CRASHREPORTER
   free(file_ids);
   file_ids = NULL;
 #endif
 
-  if (!sqlite_handle)
+  if (!sqlite_handle) {
     __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Couldn't get a handle to libmozsqlite3!");
+    return FAILURE;
+  }
 
   setup_sqlite_functions(sqlite_handle);
+  return SUCCESS;
+}
+
+static mozglueresult
+loadNSSLibs(const char *apkName)
+{
+  __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "loadNSSLibs");
+  chdir(getenv("GRE_HOME"));
+
+#ifdef MOZ_OLD_LINKER
+  if (!simple_linker_initialized) {
+    simple_linker_init();
+    simple_linker_initialized = true;
+  }
+
+  struct stat status;
+  if (!stat(apkName, &status))
+    apk_mtime = status.st_mtime;
+#endif
+
+  Zip *zip = new Zip(apkName);
+  if (!lib_mapping) {
+    lib_mapping = (struct mapping_info *)calloc(MAX_MAPPING_INFO, sizeof(*lib_mapping));
+  }
+
+#ifdef MOZ_CRASHREPORTER
+  file_ids = (char *)extractBuf("lib.id", zip);
+#endif
+
+#ifndef MOZ_OLD_LINKER
+  char *file = new char[strlen(apkName) + sizeof("!/libnss3.so")];
+  sprintf(file, "%s!/libnss3.so", apkName);
+  nss_handle = __wrap_dlopen(file, RTLD_GLOBAL | RTLD_LAZY);
+  delete [] file;
+
+  file = new char[strlen(apkName) + sizeof("!/libnspr4.so")];
+  sprintf(file, "%s!/libnspr4.so", apkName);
+  nspr_handle = __wrap_dlopen(file, RTLD_GLOBAL | RTLD_LAZY);
+  delete [] file;
+
+  file = new char[strlen(apkName) + sizeof("!/libplc4.so")];
+  sprintf(file, "%s!/libplc4.so", apkName);
+  plc_handle = __wrap_dlopen(file, RTLD_GLOBAL | RTLD_LAZY);
+  delete [] file;
+#else
+#define MOZLOAD(name) mozload("lib" name ".so", zip)
+  nspr_handle = MOZLOAD("nspr4");
+  plc_handle = MOZLOAD("plc4");
+  MOZLOAD("plds4");
+  MOZLOAD("nssutil3");
+  nss_handle = MOZLOAD("nss3");
+  MOZLOAD("ssl3");
+  MOZLOAD("smime3");
+  MOZLOAD("nssckbi");
+  MOZLOAD("freebl3");
+  MOZLOAD("softokn3");
+#undef MOZLOAD
+#endif
+
+  delete zip;
+
+#ifdef MOZ_CRASHREPORTER
+  free(file_ids);
+  file_ids = NULL;
+#endif
+
+  __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "loadNSSLibs 2");
+  if (!nss_handle) {
+    __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Couldn't get a handle to libnss3!");
+    return FAILURE;
+  }
+
+  if (!nspr_handle) {
+    __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Couldn't get a handle to libnspr4!");
+    return FAILURE;
+  }
+
+  if (!plc_handle) {
+    __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Couldn't get a handle to libplc4!");
+    return FAILURE;
+  }
+
+  __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "loadNSSLibs 3");
+  return setup_nss_functions(nss_handle, nspr_handle, plc_handle);
 }
 
 extern "C" NS_EXPORT void JNICALL
 Java_org_mozilla_gecko_GeckoAppShell_loadGeckoLibsNative(JNIEnv *jenv, jclass jGeckoAppShellClass, jstring jApkName)
 {
   const char* str;
   // XXX: java doesn't give us true UTF8, we should figure out something
   // better to do here
   str = jenv->GetStringUTFChars(jApkName, NULL);
   if (str == NULL)
     return;
 
-  loadGeckoLibs(str);
+  int res = loadGeckoLibs(str);
+  if (res != SUCCESS) {
+    JNI_Throw(jenv, "java/lang/Exception", "Error loading gecko libraries");
+  }
   jenv->ReleaseStringUTFChars(jApkName, str);
 }
 
 extern "C" NS_EXPORT void JNICALL
 Java_org_mozilla_gecko_GeckoAppShell_loadSQLiteLibsNative(JNIEnv *jenv, jclass jGeckoAppShellClass, jstring jApkName, jboolean jShouldExtract) {
   if (jShouldExtract) {
 #ifdef MOZ_OLD_LINKER
     extractLibs = 1;
@@ -807,17 +919,48 @@ Java_org_mozilla_gecko_GeckoAppShell_loa
 
   const char* str;
   // XXX: java doesn't give us true UTF8, we should figure out something
   // better to do here
   str = jenv->GetStringUTFChars(jApkName, NULL);
   if (str == NULL)
     return;
 
-  loadSQLiteLibs(str);
+  __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Load sqlite start\n");
+  mozglueresult rv = loadSQLiteLibs(str);
+  if (rv != SUCCESS) {
+      JNI_Throw(jenv, "java/lang/Exception", "Error loading sqlite libraries");
+  }
+  __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Load sqlite done\n");
+  jenv->ReleaseStringUTFChars(jApkName, str);
+}
+
+extern "C" NS_EXPORT void JNICALL
+Java_org_mozilla_gecko_GeckoAppShell_loadNSSLibsNative(JNIEnv *jenv, jclass jGeckoAppShellClass, jstring jApkName, jboolean jShouldExtract) {
+  if (jShouldExtract) {
+#ifdef MOZ_OLD_LINKER
+    extractLibs = 1;
+#else
+    putenv("MOZ_LINKER_EXTRACT=1");
+#endif
+  }
+
+  const char* str;
+  // XXX: java doesn't give us true UTF8, we should figure out something
+  // better to do here
+  str = jenv->GetStringUTFChars(jApkName, NULL);
+  if (str == NULL)
+    return;
+
+  __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Load nss start\n");
+  mozglueresult rv = loadNSSLibs(str);
+  if (rv != SUCCESS) {
+    JNI_Throw(jenv, "java/lang/Exception", "Error loading nss libraries");
+  }
+  __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Load nss done\n");
   jenv->ReleaseStringUTFChars(jApkName, str);
 }
 
 typedef void (*GeckoStart_t)(void *, const nsXREAppData *);
 
 extern "C" NS_EXPORT void JNICALL
 Java_org_mozilla_gecko_GeckoAppShell_nativeRun(JNIEnv *jenv, jclass jc, jstring jargs)
 {
@@ -831,47 +974,49 @@ Java_org_mozilla_gecko_GeckoAppShell_nat
   char *args = (char *) malloc(len + 1);
   jenv->GetStringUTFRegion(jargs, 0, len, args);
   args[len] = '\0';
   GeckoStart(args, &sAppData);
   free(args);
 }
 
 typedef int GeckoProcessType;
-typedef int nsresult;
 
-extern "C" NS_EXPORT int
+extern "C" NS_EXPORT mozglueresult
 ChildProcessInit(int argc, char* argv[])
 {
   int i;
   for (i = 0; i < (argc - 1); i++) {
     if (strcmp(argv[i], "-greomni"))
       continue;
 
     i = i + 1;
     break;
   }
 
 #ifdef MOZ_OLD_LINKER
   fillLibCache(argv[argc - 1]);
 #endif
-  loadSQLiteLibs(argv[i]);
-  loadGeckoLibs(argv[i]);
+  if (loadNSSLibs(argv[i]) != SUCCESS) {
+    return FAILURE;
+  }
+  if (loadSQLiteLibs(argv[i]) != SUCCESS) {
+    return FAILURE;
+  }
+  if (loadGeckoLibs(argv[i]) != SUCCESS) {
+    return FAILURE;
+  }
 
   // don't pass the last arg - it's only recognized by the lib cache
   argc--;
 
   typedef GeckoProcessType (*XRE_StringToChildProcessType_t)(char*);
-  typedef nsresult (*XRE_InitChildProcess_t)(int, char**, GeckoProcessType);
+  typedef mozglueresult (*XRE_InitChildProcess_t)(int, char**, GeckoProcessType);
   XRE_StringToChildProcessType_t fXRE_StringToChildProcessType =
     (XRE_StringToChildProcessType_t)__wrap_dlsym(xul_handle, "XRE_StringToChildProcessType");
   XRE_InitChildProcess_t fXRE_InitChildProcess =
     (XRE_InitChildProcess_t)__wrap_dlsym(xul_handle, "XRE_InitChildProcess");
 
   GeckoProcessType proctype = fXRE_StringToChildProcessType(argv[--argc]);
 
-  nsresult rv = fXRE_InitChildProcess(argc, argv, proctype);
-  if (rv != 0)
-    return 1;
-
-  return 0;
+  return fXRE_InitChildProcess(argc, argv, proctype);
 }
 
--- a/mozglue/android/APKOpen.h
+++ b/mozglue/android/APKOpen.h
@@ -32,35 +32,42 @@
  * 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 ***** */
 
 #ifndef APKOpen_h
 #define APKOpen_h
 
+#include <jni.h>
+
 #ifndef NS_EXPORT
 #define NS_EXPORT __attribute__ ((visibility("default")))
 #endif
 
 struct mapping_info {
   char * name;
   char * file_id;
   uintptr_t base;
   size_t len;
   size_t offset;
 };
 
 const struct mapping_info * getLibraryMapping();
 
+static const int SUCCESS = 0;
+static const int FAILURE = 1;
+void JNI_Throw(JNIEnv* jenv, const char* classname, const char* msg);
+
 #define MAX_LIB_CACHE_ENTRIES 32
 #define MAX_LIB_CACHE_NAME_LEN 32
 
 struct lib_cache_info {
   char name[MAX_LIB_CACHE_NAME_LEN];
   int fd;
   uint32_t lib_size;
   void* buffer;
 };
 
 NS_EXPORT const struct lib_cache_info * getLibraryCache();
 
+
 #endif /* APKOpen_h */
--- a/mozglue/android/Makefile.in
+++ b/mozglue/android/Makefile.in
@@ -50,24 +50,47 @@ STL_FLAGS=
 DEFINES += \
   -DANDROID_PACKAGE_NAME='"$(ANDROID_PACKAGE_NAME)"' \
   $(NULL)
 
 CPPSRCS = \
   nsGeckoUtils.cpp \
   APKOpen.cpp \
   SQLiteBridge.cpp \
+	NSSBridge.cpp \
   $(NULL)
 
 LOCAL_INCLUDES += -I$(DEPTH)/build
 LOCAL_INCLUDES += -I$(topsrcdir)/xpcom/build
 
 LOCAL_INCLUDES += -I$(srcdir)/../linker
 LOCAL_INCLUDES += -I$(topsrcdir)/toolkit/components/startup
 LOCAL_INCLUDES += -I$(topsrcdir)/db/sqlite3/src
+LOCAL_INCLUDES += -I$(topsrcdir)/base/src
+LOCAL_INCLUDES += -I$(topsrcdir)/nsprpub/lib/ds
+LOCAL_INCLUDES += -I$(topsrcdir)/nsprpub/lib/libc/include
+LOCAL_INCLUDES += -I$(topsrcdir)/nsprpub/pr/include
+LOCAL_INCLUDES += -I$(topsrcdir)/ipc/chromium/src/base/third_party/nspr
+LOCAL_INCLUDES += -I$(topsrcdir)/ipc/chromium/src
+LOCAL_INCLUDES += -I$(topsrcdir)/security/nss/lib/nss
+LOCAL_INCLUDES += -I$(topsrcdir)/security/nss/lib/util
+LOCAL_INCLUDES += -I$(topsrcdir)/security/nss/lib/softoken
+LOCAL_INCLUDES += -I$(topsrcdir)/security/nss/lib/pk11wrap
+LOCAL_INCLUDES += -I$(topsrcdir)/security/nss/cmd/ecperf
+LOCAL_INCLUDES += -I$(topsrcdir)/security/nss/lib/pkcs7
+LOCAL_INCLUDES += -I$(topsrcdir)/security/nss/lib/certdb
+LOCAL_INCLUDES += -I$(topsrcdir)/security/nss/lib/cryptohi
+LOCAL_INCLUDES += -I$(topsrcdir)/security/nss/lib/dev
+LOCAL_INCLUDES += -I$(topsrcdir)/security/nss/lib/base
+LOCAL_INCLUDES += -I$(topsrcdir)/security/nss/lib/pki
+LOCAL_INCLUDES += -I$(topsrcdir)/security/nss/lib/smime
+LOCAL_INCLUDES += -I$(topsrcdir)/security/nss/lib/freebl/
+LOCAL_INCLUDES += -I$(topsrcdir)/security/nss/lib/ssl
+LOCAL_INCLUDES += -I$(topsrcdir)/security/nss/cmd/lib/
+
 ifdef MOZ_OLD_LINKER
 DEFINES += -DMOZ_OLD_LINKER
 LOCAL_INCLUDES += -I$(topsrcdir)/other-licenses/android
 ifeq ($(CPU_ARCH),arm)
 DEFINES += -DANDROID_ARM_LINKER
 endif
 endif
 
new file mode 100644
--- /dev/null
+++ b/mozglue/android/NSSBridge.cpp
@@ -0,0 +1,300 @@
+/* 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/. */
+
+#include <stdlib.h>
+#include "dlfcn.h"
+#include "NSSBridge.h"
+#include "APKOpen.h"
+#ifdef ANDROID
+#include <jni.h>
+#include <android/log.h>
+#endif
+
+#ifndef MOZ_OLD_LINKER
+#include "ElfLoader.h"
+#endif
+
+#define DEBUG 1
+
+#ifdef DEBUG
+#define LOG(x...) __android_log_print(ANDROID_LOG_INFO, "GeckoJNI", x)
+#else
+#define LOG(x...) printf(x);
+#endif
+
+static bool initialized = false;
+
+#define NSS_WRAPPER_INT(name) name ## _t f_ ## name;
+NSS_WRAPPER_INT(NSS_Initialize)
+NSS_WRAPPER_INT(NSS_Shutdown)
+NSS_WRAPPER_INT(SECITEM_ZfreeItem)
+NSS_WRAPPER_INT(PK11SDR_Encrypt)
+NSS_WRAPPER_INT(PK11SDR_Decrypt)
+NSS_WRAPPER_INT(PK11_GetInternalKeySlot)
+NSS_WRAPPER_INT(PK11_NeedUserInit)
+NSS_WRAPPER_INT(PK11_InitPin)
+NSS_WRAPPER_INT(PR_ErrorToString)
+NSS_WRAPPER_INT(PR_GetError)
+NSS_WRAPPER_INT(PR_Free)
+NSS_WRAPPER_INT(PL_Base64Encode)
+NSS_WRAPPER_INT(PL_Base64Decode)
+NSS_WRAPPER_INT(PL_strfree)
+
+int
+setup_nss_functions(void *nss_handle,
+                        void *nspr_handle,
+                        void *plc_handle)
+{
+  __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "setup nss 1");
+  if (nss_handle == NULL || nspr_handle == NULL || plc_handle == NULL) {
+    LOG("missing handle\n");
+    return FAILURE;
+  }
+#define GETFUNC(name) f_ ## name = (name ## _t) __wrap_dlsym(nss_handle, #name); \
+                      if (!f_ ##name) return FAILURE;
+  GETFUNC(NSS_Initialize);
+  GETFUNC(NSS_Shutdown);
+  GETFUNC(PK11SDR_Encrypt);
+  GETFUNC(PK11SDR_Decrypt);
+  GETFUNC(PK11_GetInternalKeySlot);
+  GETFUNC(PK11_NeedUserInit);
+  GETFUNC(PK11_InitPin);
+  GETFUNC(SECITEM_ZfreeItem);
+#undef GETFUNC
+#define NSPRFUNC(name) f_ ## name = (name ## _t) __wrap_dlsym(nspr_handle, #name); \
+                       if (!f_ ##name) return FAILURE;
+  NSPRFUNC(PR_ErrorToString);
+  NSPRFUNC(PR_GetError);
+  NSPRFUNC(PR_Free);
+#undef NSPRFUNC
+#define PLCFUNC(name) f_ ## name = (name ## _t) __wrap_dlsym(plc_handle, #name); \
+                      if (!f_ ##name) return FAILURE;
+  PLCFUNC(PL_Base64Encode);
+  PLCFUNC(PL_Base64Decode);
+  PLCFUNC(PL_strfree);
+#undef PLCFUNC
+
+  return SUCCESS;
+}
+
+/* Throws the current NSS error. */
+static void
+throwError(JNIEnv* jenv, const char * funcString) {
+    char *msg;
+
+    PRErrorCode perr = f_PR_GetError();
+    char * errString = f_PR_ErrorToString(perr, 0);
+    asprintf(&msg, "%s returned error %d: %s\n", funcString, perr, errString);
+    LOG("Throwing error: %s\n", msg);
+
+    JNI_Throw(jenv, "java/lang/Exception", msg);
+    free(msg);
+    LOG("Error thrown\n");
+}
+
+extern "C" NS_EXPORT jstring JNICALL
+Java_org_mozilla_gecko_NSSBridge_nativeEncrypt(JNIEnv* jenv, jclass,
+                                               jstring jPath,
+                                               jstring jValue)
+{
+    jstring ret = jenv->NewStringUTF("");
+
+    const char* path;
+    path = jenv->GetStringUTFChars(jPath, NULL);
+
+    const char* value;
+    value = jenv->GetStringUTFChars(jValue, NULL);
+
+    char* result;
+    SECStatus rv = doCrypto(jenv, path, value, &result, true);
+    if (rv == SECSuccess) {
+      ret = jenv->NewStringUTF(result);
+      free(result);
+    }
+
+    jenv->ReleaseStringUTFChars(jValue, value);
+    jenv->ReleaseStringUTFChars(jPath, path);
+
+    return ret;
+}
+
+extern "C" NS_EXPORT jstring JNICALL
+Java_org_mozilla_gecko_NSSBridge_nativeDecrypt(JNIEnv* jenv, jclass,
+                                               jstring jPath,
+                                               jstring jValue)
+{
+    jstring ret = jenv->NewStringUTF("");
+
+    const char* path;
+    path = jenv->GetStringUTFChars(jPath, NULL);
+
+    const char* value;
+    value = jenv->GetStringUTFChars(jValue, NULL);
+
+    char* result;
+    SECStatus rv = doCrypto(jenv, path, value, &result, false);
+    if (rv == SECSuccess) {
+      ret = jenv->NewStringUTF(result);
+      free(result);
+    }
+
+    jenv->ReleaseStringUTFChars(jValue, value);
+    jenv->ReleaseStringUTFChars(jPath, path);
+
+    return ret;
+}
+
+
+/* Encrypts or decrypts a string. result should be freed with free() when done */
+SECStatus
+doCrypto(JNIEnv* jenv, const char *path, const char *value, char** result, bool encrypt)
+{
+    SECStatus rv;
+    PK11SlotInfo *slot;
+    if (!initialized) {
+      LOG("initialize crypto %s\n", path);
+      rv = f_NSS_Initialize(path, "", "", "secmod.db", NSS_INIT_NOROOTINIT);
+      if (rv != SECSuccess) {
+          throwError(jenv, "NSS_Initialize");
+          return rv;
+      }
+      initialized = true;
+    }
+
+    slot = f_PK11_GetInternalKeySlot();
+    if (!slot) {
+      throwError(jenv, "PK11_GetInternalKeySlot");
+      return SECFailure;
+    }
+
+    if (f_PK11_NeedUserInit(slot)) {
+      LOG("Initializing key3.db with default blank password.");
+      rv = f_PK11_InitPin(slot, NULL, NULL);
+      if (rv != SECSuccess) {
+        throwError(jenv, "PK11_InitPin");
+        return rv;
+      }
+    }
+
+    SECItem request;
+    SECItem reply;
+
+    reply.data = 0;
+    reply.len = 0;
+
+    if (encrypt) {
+      LOG("encrypting %s\n", value);
+      request.data = (unsigned char*)value;
+      request.len = strlen(value);
+
+      SECItem keyid;
+      keyid.data = 0;
+      keyid.len = 0;
+      rv = f_PK11SDR_Encrypt(&keyid, &request, &reply, NULL);
+
+      if (rv != SECSuccess) {
+        throwError(jenv, "PK11SDR_Encrypt");
+        goto done;
+      }
+
+      rv = encode(reply.data, reply.len, result);
+      if (rv != SECSuccess) {
+          throwError(jenv, "encode");
+          goto done;
+      }
+      LOG("encrypted %s\n", *result);
+    } else {
+      LOG("decoding %s\n", value);
+      rv = decode(value, &request.data, (PRInt32*)&request.len);
+      if (rv != SECSuccess) {
+          throwError(jenv, "decode");
+          return rv;
+      }
+
+      rv = f_PK11SDR_Decrypt(&request, &reply, NULL);
+      if (rv != SECSuccess) {
+        throwError(jenv, "PK11SDR_Decrypt");
+        goto done;
+      }
+
+      *result = (char *)malloc(reply.len);
+      (*result)[reply.len] = '\0';
+      strncpy(*result, (char *)reply.data, reply.len);
+      //asprintf(result, "%s", (char *)reply.data);
+
+      LOG("decoded %i letters %s\n", reply.len, *result);
+      free(request.data);
+    }
+
+done:
+    f_SECITEM_ZfreeItem(&reply, false);
+    return rv;
+}
+
+/*
+ * Base64 encodes the data passed in. The caller must deallocate _retval using free();
+ */
+SECStatus
+encode(const unsigned char *data, PRInt32 dataLen, char **_retval)
+{
+  SECStatus rv = SECSuccess;
+  char *encoded = f_PL_Base64Encode((const char *)data, dataLen, NULL);
+  if (!encoded)
+    rv = SECFailure;
+  if (!*encoded)
+    rv = SECFailure;
+
+  if (rv == SECSuccess) {
+    *_retval = (char *)malloc(strlen(encoded));
+    strcpy(*_retval, encoded);
+  }
+
+  if (encoded) {
+    f_PR_Free(encoded);
+  }
+
+  return rv;
+}
+
+/*
+ * Base64 decodes the data passed in. The caller must deallocate result using free();
+ */
+SECStatus
+decode(const char *data, unsigned char **result, PRInt32 *length)
+{
+  SECStatus rv = SECSuccess;
+  PRUint32 len = strlen(data);
+  int adjust = 0;
+
+  /* Compute length adjustment */
+  if (len > 0 && data[len-1] == '=') {
+    adjust++;
+    if (data[len-2] == '=') adjust++;
+  }
+
+  char *decoded;
+  decoded = f_PL_Base64Decode(data, len, NULL);
+  if (!decoded) {
+    return SECFailure;
+  }
+
+  LOG("xxx Decoded: %s\n", decoded);
+
+  if (!*decoded) {
+    return SECFailure;
+  }
+
+  *length = (len*3)/4 - adjust;
+  *result = (unsigned char*)malloc((size_t)len);
+
+  if (!*result) {
+    rv = SECFailure;
+  } else {
+    memcpy((char*)*result, decoded, len);
+  }
+  f_PR_Free(decoded);
+  return rv;
+}
+
+
new file mode 100644
--- /dev/null
+++ b/mozglue/android/NSSBridge.h
@@ -0,0 +1,36 @@
+#ifndef NSSBridge_h
+#define NSSBridge_h
+
+#include "nss.h"
+#include "seccomon.h"
+#include "secmodt.h"
+#include "secutil.h"
+#include "pk11func.h"
+#include <jni.h>
+
+int setup_nss_functions(void *nss_handle, void *nssutil_handle, void *plc_handle);
+
+#define NSS_WRAPPER(name, return_type, args...) \
+typedef return_type (*name ## _t)(args);  \
+extern name ## _t f_ ## name;
+
+NSS_WRAPPER(NSS_Initialize, SECStatus, const char*, const char*, const char*, const char*, PRUint32)
+NSS_WRAPPER(NSS_Shutdown, void, void)
+NSS_WRAPPER(PK11SDR_Encrypt, SECStatus, SECItem *, SECItem *, SECItem *, void *)
+NSS_WRAPPER(PK11SDR_Decrypt, SECStatus, SECItem *, SECItem *, void *)
+NSS_WRAPPER(SECITEM_ZfreeItem, void, SECItem*, PRBool)
+NSS_WRAPPER(PR_ErrorToString, char *, PRErrorCode, PRLanguageCode)
+NSS_WRAPPER(PR_GetError, PRErrorCode, void)
+NSS_WRAPPER(PR_Free, PRErrorCode, char *)
+NSS_WRAPPER(PL_Base64Encode, char*, const char*, PRUint32, char*)
+NSS_WRAPPER(PL_Base64Decode, char*, const char*, PRUint32, char*)
+NSS_WRAPPER(PL_strfree, void, char*)
+NSS_WRAPPER(PK11_GetInternalKeySlot, PK11SlotInfo *, void)
+NSS_WRAPPER(PK11_NeedUserInit, PRBool, PK11SlotInfo *)
+NSS_WRAPPER(PK11_InitPin, SECStatus, PK11SlotInfo*, const char*, const char*)
+
+bool setPassword(PK11SlotInfo *slot);
+SECStatus doCrypto(JNIEnv* jenv, const char *path, const char *value, char** result, bool doEncrypt);
+SECStatus encode(const unsigned char *data, PRInt32 dataLen, char **_retval);
+SECStatus decode(const char *data, unsigned char **result, PRInt32 * _retval);
+#endif /* NSS_h */
--- a/mozglue/android/SQLiteBridge.cpp
+++ b/mozglue/android/SQLiteBridge.cpp
@@ -97,31 +97,16 @@ static jclass stringClass;
 static jclass objectClass;
 static jclass byteBufferClass;
 static jclass cursorClass;
 static jmethodID jByteBufferAllocateDirect;
 static jmethodID jCursorConstructor;
 static jmethodID jCursorAddRow;
 
 static void
-JNI_Throw(JNIEnv* jenv, const char* name, const char* msg)
-{
-    jclass cls = jenv->FindClass(name);
-    if (cls == NULL) {
-        LOG("Couldn't find exception class (or exception pending)\n");
-        return;
-    }
-    int rc = jenv->ThrowNew(cls, msg);
-    if (rc < 0) {
-        LOG("Error throwing exception\n");
-    }
-    jenv->DeleteLocalRef(cls);
-}
-
-static void
 JNI_Setup(JNIEnv* jenv)
 {
     if (initialized) return;
 
     jclass lObjectClass       = jenv->FindClass("java/lang/Object");
     jclass lStringClass       = jenv->FindClass("java/lang/String");
     jclass lByteBufferClass   = jenv->FindClass("java/nio/ByteBuffer");
     jclass lCursorClass       = jenv->FindClass("org/mozilla/gecko/sqlite/MatrixBlobCursor");