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 90074 d0c82cb6eb28a8a4e67b00817ef358d98ed6b960
parent 90073 3689bb4199d85060c1ecd481fe3e9c1dc5ac764e
child 90075 b295c8825acd15c76dc25bf62caad1691fa0c94b
push id975
push userffxbld
push dateTue, 13 Mar 2012 21:39:16 +0000
treeherdermozilla-aurora@99faebf9dc36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersblassey, bsmith
bugs718760
milestone13.0a1
Bug 718760 - Crypto for the java passwords provider. r=blassey,bsmith
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/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>
@@ -166,30 +174,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);
+    public static native void loadNSSLibsNative(String apkName);
     public static native void onChangeNetworkLinkStatus(String status);
 
     public static void reportJavaCrash(Throwable e) {
         Log.e(LOGTAG, "top level exception", e);
         StringWriter sw = new StringWriter();
         PrintWriter pw = new PrintWriter(sw);
         e.printStackTrace(pw);
         pw.flush();
@@ -341,23 +343,35 @@ public class GeckoAppShell
 
     public static void loadSQLiteLibs(Context context, String apkName) {
         if (sSQLiteLibsLoaded)
             return;
         synchronized(sSQLiteLibsLoaded) {
             if (sSQLiteLibsLoaded)
                 return;
             loadMozGlue();
-            // the extract libs parameter is being removed in bug 732069
             loadLibsSetup(context);
             loadSQLiteLibsNative(apkName);
             sSQLiteLibsLoaded = true;
         }
     }
 
+    public static void loadNSSLibs(Context context, String apkName) {
+        if (sNSSLibsLoaded)
+            return;
+        synchronized(sNSSLibsLoaded) {
+            if (sNSSLibsLoaded)
+                return;
+            loadMozGlue();
+            loadLibsSetup(context);
+            loadNSSLibsNative(apkName);
+            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());
 
@@ -106,10 +107,11 @@ public class GeckoThread extends Thread 
 
             GeckoAppShell.runGecko(app.getApplication().getPackageResourcePath(),
                                    mIntent.getStringExtra("args"),
                                    mUri,
                                    mRestoreSession);
         } catch (Exception e) {
             GeckoAppShell.reportJavaCrash(e);
         }
+
     }
 }
--- a/mobile/android/base/Makefile.in
+++ b/mobile/android/base/Makefile.in
@@ -83,22 +83,24 @@ 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 \
+  NSSBridge.java \
   ProfileMigrator.java \
   PromptService.java \
   sqlite/ByteBufferInputStream.java \
   sqlite/MatrixBlobCursor.java \
   sqlite/SQLiteBridge.java \
   sqlite/SQLiteBridgeException.java \
   SetupScreen.java \
   SurfaceLockInfo.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,19 @@ 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;
 
 #if defined(MOZ_CRASHREPORTER) || defined(MOZ_OLD_LINKER)
 static void
 extractLib(Zip::Stream &s, void * dest)
 {
   z_stream strm = {
     next_in: (Bytef *)s.GetBuffer(),
     avail_in: s.GetSize(),
@@ -544,17 +567,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"));
 
   struct timeval t0, t1;
   gettimeofday(&t0, 0);
   struct rusage usage1;
   getrusage(RUSAGE_THREAD, &usage1);
@@ -569,39 +592,38 @@ loadGeckoLibs(const char *apkName)
   char *file = new char[strlen(apkName) + sizeof("!/libxpcom.so")];
   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");
+  if (!MOZLOAD("mozalloc")) return FAILURE;
+  if (!MOZLOAD("plds4")) return FAILURE;
+  if (!MOZLOAD("nssutil3")) return FAILURE;
+  if (!MOZLOAD("ssl3")) return FAILURE;
+  if (!MOZLOAD("smime3")) return FAILURE;
   xul_handle = MOZLOAD("xul");
-  MOZLOAD("xpcom");
-  MOZLOAD("nssckbi");
-  MOZLOAD("freebl3");
-  MOZLOAD("softokn3");
+  if (!MOZLOAD("xpcom")) return FAILURE;
+  if (!MOZLOAD("nssckbi")) return FAILURE;
+  if (!MOZLOAD("freebl3")) return FAILURE;
+  if (!MOZLOAD("softokn3")) return FAILURE;
 #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);
@@ -635,19 +657,20 @@ 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();
 #endif
 
   RefPtr<Zip> zip = new Zip(apkName);
@@ -668,47 +691,147 @@ 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
+  simple_linker_init();
+
+  struct stat status;
+  if (!stat(apkName, &status))
+    apk_mtime = status.st_mtime;
+#endif
+
+  Zip *zip = new Zip(apkName);
+  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)
+  nss_handle  = MOZLOAD("libnss3");
+  nspr_handle = MOZLOAD("libnspr4");
+  plc_handle  = MOZLOAD("libplc4");
+#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) {
   putenv("MOZ_LINKER_EXTRACT=1");
   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) {
+  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)
 {
@@ -722,47 +845,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");