Bug 946857 - part 3: Sync password repo related changes for logins storage r?nalexander draft
authorvivek <vivekb.balakrishnan@gmail.com>
Thu, 28 Jan 2016 23:42:16 +0200
changeset 330725 0f8b41b1957cc027e2510df910dce2f75d73d75f
parent 330724 6f605a17e802eec9c09d7c535e7b4b12d0c8a830
child 514224 518cc466b780d8c527dcc13d51207adbbc263f81
push id10812
push uservivekb.balakrishnan@gmail.com
push dateFri, 12 Feb 2016 18:16:42 +0000
reviewersnalexander
bugs946857
milestone47.0a1
Bug 946857 - part 3: Sync password repo related changes for logins storage r?nalexander * PasswordsProvider conditionally swapped with LoginsProvider for sync based on build flag * Fixed timing issue in passwordRepository tests. MozReview-Commit-ID: DPdsnXQ2TDw
mobile/android/services/src/main/java/org/mozilla/gecko/sync/repositories/android/BrowserContractHelpers.java
mobile/android/services/src/main/java/org/mozilla/gecko/sync/repositories/android/PasswordsRepositorySession.java
mobile/android/tests/background/junit3/src/org/mozilla/gecko/background/db/TestPasswordsRepository.java
mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testPasswordEncrypt.java
mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testPasswordProvider.java
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/repositories/android/BrowserContractHelpers.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/repositories/android/BrowserContractHelpers.java
@@ -3,16 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.sync.repositories.android;
 
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
 
+import org.mozilla.gecko.AppConstants;
 import org.mozilla.gecko.db.BrowserContract;
 import org.mozilla.gecko.sync.setup.Constants;
 
 import android.net.Uri;
 
 public class BrowserContractHelpers extends BrowserContract {
 
   protected static Uri withSyncAndDeletedAndProfile(Uri u) {
@@ -29,39 +30,24 @@ public class BrowserContractHelpers exte
         .build();
   }
 
   public static final Uri BOOKMARKS_CONTENT_URI            = withSyncAndDeletedAndProfile(Bookmarks.CONTENT_URI);
   public static final Uri BOOKMARKS_PARENTS_CONTENT_URI    = withSyncAndDeletedAndProfile(Bookmarks.PARENTS_CONTENT_URI);
   public static final Uri BOOKMARKS_POSITIONS_CONTENT_URI  = withSyncAndDeletedAndProfile(Bookmarks.POSITIONS_CONTENT_URI);
   public static final Uri HISTORY_CONTENT_URI              = withSyncAndDeletedAndProfile(History.CONTENT_URI);
   public static final Uri SCHEMA_CONTENT_URI               = withSyncAndDeletedAndProfile(Schema.CONTENT_URI);
-  public static final Uri PASSWORDS_CONTENT_URI            = withSyncAndDeletedAndProfile(Passwords.CONTENT_URI);
-  public static final Uri DELETED_PASSWORDS_CONTENT_URI    = withSyncAndDeletedAndProfile(DeletedPasswords.CONTENT_URI);
+  public static final Uri PASSWORDS_CONTENT_URI;
+  public static final Uri DELETED_PASSWORDS_CONTENT_URI;
   public static final Uri FORM_HISTORY_CONTENT_URI         = withSyncAndProfile(FormHistory.CONTENT_URI);
   public static final Uri DELETED_FORM_HISTORY_CONTENT_URI = withSyncAndProfile(DeletedFormHistory.CONTENT_URI);
   public static final Uri TABS_CONTENT_URI                 = withSyncAndProfile(Tabs.CONTENT_URI);
   public static final Uri CLIENTS_CONTENT_URI              = withSyncAndProfile(Clients.CONTENT_URI);
 
-  public static final String[] PasswordColumns = new String[] {
-    Passwords.ID,
-    Passwords.HOSTNAME,
-    Passwords.HTTP_REALM,
-    Passwords.FORM_SUBMIT_URL,
-    Passwords.USERNAME_FIELD,
-    Passwords.PASSWORD_FIELD,
-    Passwords.ENCRYPTED_USERNAME,
-    Passwords.ENCRYPTED_PASSWORD,
-    Passwords.ENC_TYPE,
-    Passwords.TIME_CREATED,
-    Passwords.TIME_LAST_USED,
-    Passwords.TIME_PASSWORD_CHANGED,
-    Passwords.TIMES_USED,
-    Passwords.GUID
-  };
+  public static final String[] PasswordColumns;
 
   public static final String[] HistoryColumns = new String[] {
     CommonColumns._ID,
     SyncColumns.GUID,
     SyncColumns.DATE_CREATED,
     SyncColumns.DATE_MODIFIED,
     SyncColumns.IS_DELETED,
     History.TITLE,
@@ -91,21 +77,73 @@ public class BrowserContractHelpers exte
     FormHistory.GUID,
     FormHistory.FIELD_NAME,
     FormHistory.VALUE,
     FormHistory.TIMES_USED,
     FormHistory.FIRST_USED,
     FormHistory.LAST_USED
   };
 
-  public static final String[] DeletedColumns = new String[] {
-    BrowserContract.DeletedColumns.ID,
-    BrowserContract.DeletedColumns.GUID,
-    BrowserContract.DeletedColumns.TIME_DELETED
-  };
+  public static final String[] DeletedColumns;
+
+  static {
+    if (AppConstants.MOZ_ANDROID_LOGINS_STORAGE_USE) {
+      PASSWORDS_CONTENT_URI            = withSyncAndDeletedAndProfile(Logins.CONTENT_URI);
+      DELETED_PASSWORDS_CONTENT_URI    = withSyncAndDeletedAndProfile(DeletedLogins.CONTENT_URI);
+
+      PasswordColumns = new String[] {
+          Logins._ID,
+          Logins.HOSTNAME,
+          Logins.HTTP_REALM,
+          Logins.FORM_SUBMIT_URL,
+          Logins.USERNAME_FIELD,
+          Logins.PASSWORD_FIELD,
+          Logins.ENCRYPTED_USERNAME,
+          Logins.ENCRYPTED_PASSWORD,
+          Logins.ENC_TYPE,
+          Logins.TIME_CREATED,
+          Logins.TIME_LAST_USED,
+          Logins.TIME_PASSWORD_CHANGED,
+          Logins.TIMES_USED,
+          Logins.GUID
+      };
+
+      DeletedColumns= new String[] {
+          BrowserContract.DeletedLogins._ID,
+          BrowserContract.DeletedLogins.GUID,
+          BrowserContract.DeletedLogins.TIME_DELETED
+      };
+    } else {
+      PASSWORDS_CONTENT_URI            = withSyncAndDeletedAndProfile(Passwords.CONTENT_URI);
+      DELETED_PASSWORDS_CONTENT_URI    = withSyncAndDeletedAndProfile(DeletedPasswords.CONTENT_URI);
+
+      PasswordColumns = new String[] {
+          Passwords.ID,
+          Passwords.HOSTNAME,
+          Passwords.HTTP_REALM,
+          Passwords.FORM_SUBMIT_URL,
+          Passwords.USERNAME_FIELD,
+          Passwords.PASSWORD_FIELD,
+          Passwords.ENCRYPTED_USERNAME,
+          Passwords.ENCRYPTED_PASSWORD,
+          Passwords.ENC_TYPE,
+          Passwords.TIME_CREATED,
+          Passwords.TIME_LAST_USED,
+          Passwords.TIME_PASSWORD_CHANGED,
+          Passwords.TIMES_USED,
+          Passwords.GUID
+      };
+
+      DeletedColumns= new String[] {
+          BrowserContract.DeletedColumns.ID,
+          BrowserContract.DeletedColumns.GUID,
+          BrowserContract.DeletedColumns.TIME_DELETED
+      };
+    }
+  }
 
   // Mapping from Sync types to Fennec types.
   public static final String[] BOOKMARK_TYPE_CODE_TO_STRING = {
     // Observe omissions: "microsummary", "item".
     "folder", "bookmark", "separator", "livemark", "query"
   };
   private static final int MAX_BOOKMARK_TYPE_CODE = BOOKMARK_TYPE_CODE_TO_STRING.length - 1;
   public static final Map<String, Integer> BOOKMARK_TYPE_STRING_TO_CODE;
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/repositories/android/PasswordsRepositorySession.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/repositories/android/PasswordsRepositorySession.java
@@ -2,20 +2,22 @@
  * 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.sync.repositories.android;
 
 import java.util.ArrayList;
 import java.util.List;
 
+import org.mozilla.gecko.AppConstants;
 import org.mozilla.gecko.background.common.log.Logger;
 import org.mozilla.gecko.db.BrowserContract;
 import org.mozilla.gecko.db.BrowserContract.DeletedColumns;
-import org.mozilla.gecko.db.BrowserContract.DeletedPasswords;
+import org.mozilla.gecko.db.BrowserContract.DeletedLogins;
+import org.mozilla.gecko.db.BrowserContract.Logins;
 import org.mozilla.gecko.db.BrowserContract.Passwords;
 import org.mozilla.gecko.sync.repositories.InactiveSessionException;
 import org.mozilla.gecko.sync.repositories.NoStoreDelegateException;
 import org.mozilla.gecko.sync.repositories.NullCursorException;
 import org.mozilla.gecko.sync.repositories.RecordFilter;
 import org.mozilla.gecko.sync.repositories.Repository;
 import org.mozilla.gecko.sync.repositories.StoreTrackingRepositorySession;
 import org.mozilla.gecko.sync.repositories.android.RepoUtils.QueryHelper;
@@ -33,16 +35,77 @@ import android.content.ContentValues;
 import android.content.Context;
 import android.database.Cursor;
 import android.net.Uri;
 import android.os.RemoteException;
 
 public class PasswordsRepositorySession extends
     StoreTrackingRepositorySession {
 
+  private static final String PASSWORDS_HOSTNAME;
+  private static final String PASSWORDS_HTTP_REALM;
+  private static final String PASSWORDS_FORM_SUBMIT_URL;
+  private static final String PASSWORDS_USERNAME_FIELD;
+  private static final String PASSWORDS_PASSWORD_FIELD;
+  private static final String PASSWORDS_ENC_TYPE;
+  private static final String PASSWORDS_ENCRYPTED_USERNAME;
+  private static final String PASSWORDS_ENCRYPTED_PASSWORD;
+  private static final String PASSWORDS_TIME_CREATED;
+  private static final String PASSWORDS_TIME_LAST_USED;
+  private static final String PASSWORDS_TIMES_USED;
+  private static final String PASSWORDS_GUID;
+  private static final String PASSWORDS_TIME_PASSWORD_CHANGED;
+  private static final String PASSWORDS_ID;
+  private static final String DELETED_PASSWORDS_GUID;
+  private static final String DELETED_PASSWORDS_TIME_DELETED;
+  private static final String DELETED_PASSWORDS_ID;
+  private static final Uri AUTHORITY_URI;
+
+  static {
+    if (AppConstants.MOZ_ANDROID_LOGINS_STORAGE_USE) {
+      PASSWORDS_HOSTNAME = Logins.HOSTNAME;
+      PASSWORDS_HTTP_REALM = Logins.HTTP_REALM;
+      PASSWORDS_FORM_SUBMIT_URL = Logins.FORM_SUBMIT_URL;
+      PASSWORDS_USERNAME_FIELD = Logins.USERNAME_FIELD;
+      PASSWORDS_PASSWORD_FIELD = Logins.PASSWORD_FIELD;
+      PASSWORDS_ENC_TYPE = Logins.ENC_TYPE;
+      PASSWORDS_ENCRYPTED_USERNAME = Logins.ENCRYPTED_USERNAME;
+      PASSWORDS_ENCRYPTED_PASSWORD = Logins.ENCRYPTED_PASSWORD;
+      PASSWORDS_TIME_CREATED = Logins.TIME_CREATED;
+      PASSWORDS_TIME_LAST_USED = Logins.TIME_LAST_USED;
+      PASSWORDS_TIMES_USED = Logins.TIMES_USED;
+      PASSWORDS_GUID = Logins.GUID;
+      PASSWORDS_TIME_PASSWORD_CHANGED = Logins.TIME_PASSWORD_CHANGED;
+      PASSWORDS_ID = Logins._ID;
+      DELETED_PASSWORDS_GUID = DeletedLogins.GUID;
+      DELETED_PASSWORDS_TIME_DELETED = DeletedLogins.TIME_DELETED;
+      DELETED_PASSWORDS_ID = DeletedLogins._ID;
+      AUTHORITY_URI = BrowserContract.LOGINS_AUTHORITY_URI;
+    } else {
+      PASSWORDS_HOSTNAME = Passwords.HOSTNAME;
+      PASSWORDS_HTTP_REALM = Passwords.HTTP_REALM;
+      PASSWORDS_FORM_SUBMIT_URL = Passwords.FORM_SUBMIT_URL;
+      PASSWORDS_USERNAME_FIELD = Passwords.USERNAME_FIELD;
+      PASSWORDS_PASSWORD_FIELD = Passwords.PASSWORD_FIELD;
+      PASSWORDS_ENC_TYPE = Passwords.ENC_TYPE;
+      PASSWORDS_ENCRYPTED_USERNAME = Passwords.ENCRYPTED_USERNAME;
+      PASSWORDS_ENCRYPTED_PASSWORD = Passwords.ENCRYPTED_PASSWORD;
+      PASSWORDS_TIME_CREATED = Passwords.TIME_CREATED;
+      PASSWORDS_TIME_LAST_USED = Passwords.TIME_LAST_USED;
+      PASSWORDS_TIMES_USED = Passwords.TIMES_USED;
+      PASSWORDS_GUID = Passwords.GUID;
+      PASSWORDS_TIME_PASSWORD_CHANGED = Passwords.TIME_PASSWORD_CHANGED;
+      PASSWORDS_ID = Passwords.ID;
+      DELETED_PASSWORDS_GUID = DeletedColumns.GUID;
+      DELETED_PASSWORDS_TIME_DELETED = DeletedColumns.TIME_DELETED;
+      DELETED_PASSWORDS_ID = DeletedColumns.ID;
+      AUTHORITY_URI = BrowserContract.PASSWORDS_AUTHORITY_URI;
+    }
+  }
+
   public static class PasswordsRepository extends Repository {
     @Override
     public void createSession(RepositorySessionCreationDelegate delegate,
         Context context) {
       PasswordsRepositorySession session = new PasswordsRepositorySession(PasswordsRepository.this, context);
       final RepositorySessionCreationDelegate deferredCreationDelegate = delegate.deferredCreationDelegate();
       deferredCreationDelegate.onSessionCreated(session);
     }
@@ -57,24 +120,24 @@ public class PasswordsRepositorySession 
 
   private final Context context;
 
   public PasswordsRepositorySession(Repository repository, Context context) {
     super(repository);
     this.context = context;
     this.passwordsHelper        = new QueryHelper(context, BrowserContractHelpers.PASSWORDS_CONTENT_URI, LOG_TAG);
     this.deletedPasswordsHelper = new QueryHelper(context, BrowserContractHelpers.DELETED_PASSWORDS_CONTENT_URI, LOG_TAG);
-    this.passwordsProvider      = context.getContentResolver().acquireContentProviderClient(BrowserContract.PASSWORDS_AUTHORITY_URI);
+    this.passwordsProvider      = context.getContentResolver().acquireContentProviderClient(AUTHORITY_URI);
   }
 
-  private static final String[] GUID_COLS = new String[] { Passwords.GUID };
-  private static final String[] DELETED_GUID_COLS = new String[] { DeletedColumns.GUID };
+  private static final String[] GUID_COLS = new String[] { PASSWORDS_GUID };
+  private static final String[] DELETED_GUID_COLS = new String[] { DELETED_PASSWORDS_GUID };
 
-  private static final String WHERE_GUID_IS = Passwords.GUID + " = ?";
-  private static final String WHERE_DELETED_GUID_IS = DeletedPasswords.GUID + " = ?";
+  private static final String WHERE_GUID_IS = PASSWORDS_GUID + " = ?";
+  private static final String WHERE_DELETED_GUID_IS = DELETED_PASSWORDS_GUID + " = ?";
 
   @Override
   public void guidsSince(final long timestamp, final RepositorySessionGuidsSinceDelegate delegate) {
     final Runnable guidsSinceRunnable = new Runnable() {
       @Override
       public void run() {
 
         if (!isActive()) {
@@ -85,31 +148,31 @@ public class PasswordsRepositorySession 
         // Checks succeeded, now get GUIDs.
         final List<String> guids = new ArrayList<String>();
         try {
           Logger.debug(LOG_TAG, "Fetching guidsSince from data table.");
           final Cursor data = passwordsHelper.safeQuery(passwordsProvider, ".getGUIDsSince", GUID_COLS, dateModifiedWhere(timestamp), null, null);
           try {
             if (data.moveToFirst()) {
               while (!data.isAfterLast()) {
-                guids.add(RepoUtils.getStringFromCursor(data, Passwords.GUID));
+                guids.add(RepoUtils.getStringFromCursor(data, PASSWORDS_GUID));
                 data.moveToNext();
               }
             }
           } finally {
             data.close();
           }
 
           // Fetch guids from deleted table.
           Logger.debug(LOG_TAG, "Fetching guidsSince from deleted table.");
           final Cursor deleted = deletedPasswordsHelper.safeQuery(passwordsProvider, ".getGUIDsSince", DELETED_GUID_COLS, dateModifiedWhereDeleted(timestamp), null, null);
           try {
             if (deleted.moveToFirst()) {
               while (!deleted.isAfterLast()) {
-                guids.add(RepoUtils.getStringFromCursor(deleted, DeletedColumns.GUID));
+                guids.add(RepoUtils.getStringFromCursor(deleted, DELETED_PASSWORDS_GUID));
                 deleted.moveToNext();
               }
             }
           } finally {
             deleted.close();
           }
         } catch (Exception e) {
           Logger.error(LOG_TAG, "Exception in fetch.");
@@ -509,27 +572,27 @@ public class PasswordsRepositorySession 
 
   /**
    * Constructs the DB query string for entry age for deleted records.
    *
    * @param timestamp
    * @return String DB query string for dates to fetch.
    */
   private static String dateModifiedWhereDeleted(long timestamp) {
-    return DeletedColumns.TIME_DELETED + " >= " + Long.toString(timestamp);
+    return DELETED_PASSWORDS_TIME_DELETED + " >= " + Long.toString(timestamp);
   }
 
   /**
    * Constructs the DB query string for entry age for (undeleted) records.
    *
    * @param timestamp
    * @return String DB query string for dates to fetch.
    */
   private static String dateModifiedWhere(long timestamp) {
-    return Passwords.TIME_PASSWORD_CHANGED + " >= " + Long.toString(timestamp);
+    return PASSWORDS_TIME_PASSWORD_CHANGED + " >= " + Long.toString(timestamp);
   }
 
 
   /**
    * Fetch from the cursor with the given parameters, invoking
    * delegate callbacks and closing the cursor.
    * Returns true on success, false if failure was signaled.
    *
@@ -592,38 +655,71 @@ public class PasswordsRepositorySession 
       }
     } finally {
       deleted.close();
     }
 
     return null;
   }
 
-  private static final String WHERE_RECORD_DATA =
-    Passwords.HOSTNAME        + " = ? AND " +
-    Passwords.HTTP_REALM      + " = ? AND " +
-    Passwords.FORM_SUBMIT_URL + " = ? AND " +
-    Passwords.USERNAME_FIELD  + " = ? AND " +
-    Passwords.PASSWORD_FIELD  + " = ?";
+  private static final String WHERE_RECORD_DATA;
+  static {
+    if (AppConstants.MOZ_ANDROID_LOGINS_STORAGE_USE) {
+      WHERE_RECORD_DATA = PASSWORDS_HOSTNAME + " = ? AND " +
+          PASSWORDS_USERNAME_FIELD + " = ? AND " +
+          PASSWORDS_PASSWORD_FIELD + " = ?";
+    } else {
+      WHERE_RECORD_DATA = PASSWORDS_HOSTNAME + " = ? AND " +
+          PASSWORDS_HTTP_REALM + " = ? AND " +
+          PASSWORDS_FORM_SUBMIT_URL + " = ? AND " +
+          PASSWORDS_USERNAME_FIELD + " = ? AND " +
+          PASSWORDS_PASSWORD_FIELD + " = ?";
+    }
+  }
 
   private PasswordRecord findExistingRecord(PasswordRecord record) throws NullCursorException, RemoteException {
     PasswordRecord foundRecord = null;
     Cursor cursor = null;
     // Only check the data table.
     // We can't encrypt username directly for query, so run a more general query and then filter.
-    final String[] whereArgs = new String[] {
-      record.hostname,
-      record.httpRealm,
-      record.formSubmitURL,
-      record.usernameField,
-      record.passwordField
-    };
+    final String[] whereArgs;
+    final String whereRecordData;
+
+    // Android CP does not support null as bind parameter, so split the query.
+    if (AppConstants.MOZ_ANDROID_LOGINS_STORAGE_USE) {
+      String condition = WHERE_RECORD_DATA;
+      List<String> whereArgslist = new ArrayList<>();
+      whereArgslist.add(record.hostname);
+      whereArgslist.add(record.usernameField);
+      whereArgslist.add(record.passwordField);
+
+      if (record.httpRealm != null) {
+        whereArgslist.add(record.httpRealm);
+        condition += " AND " + PASSWORDS_HTTP_REALM + " = ?";
+      }
+
+      if (record.formSubmitURL != null) {
+        whereArgslist.add(record.formSubmitURL);
+        condition += " AND " + PASSWORDS_FORM_SUBMIT_URL + " = ?";
+      }
+      whereArgs = whereArgslist.toArray(new String[0]);
+      whereRecordData = condition;
+    } else {
+      whereRecordData = WHERE_RECORD_DATA;
+      whereArgs= new String[] {
+          record.hostname,
+          record.httpRealm,
+          record.formSubmitURL,
+          record.usernameField,
+          record.passwordField
+      };
+    }
 
     try {
-      cursor = passwordsHelper.safeQuery(passwordsProvider, ".findRecord", getAllColumns(), WHERE_RECORD_DATA, whereArgs, null);
+      cursor = passwordsHelper.safeQuery(passwordsProvider, ".findRecord", getAllColumns(), whereRecordData, whereArgs, null);
       while (cursor.moveToNext()) {
         foundRecord = passwordRecordFromCursor(cursor);
 
         // We don't directly query for username because the
         // username/password values are encrypted in the db.
         // We don't have the keys for encrypting our query,
         // so we run a more general query and then filter
         // the returned records for a matching username.
@@ -661,65 +757,70 @@ public class PasswordsRepositorySession 
    *        true if creating a deleted Record, false if otherwise.
    * @return
    *        PasswordRecord populated from Cursor.
    */
   private static PasswordRecord passwordRecordFromCursor(Cursor cur) {
     if (cur.isAfterLast()) {
       return null;
     }
-    String guid = RepoUtils.getStringFromCursor(cur, BrowserContract.Passwords.GUID);
-    long lastModified = RepoUtils.getLongFromCursor(cur, BrowserContract.Passwords.TIME_PASSWORD_CHANGED);
+    String guid = RepoUtils.getStringFromCursor(cur, PASSWORDS_GUID);
+    long lastModified = RepoUtils.getLongFromCursor(cur, PASSWORDS_TIME_PASSWORD_CHANGED);
 
     PasswordRecord rec = new PasswordRecord(guid, COLLECTION, lastModified, false);
-    rec.id = RepoUtils.getStringFromCursor(cur, BrowserContract.Passwords.ID);
-    rec.hostname = RepoUtils.getStringFromCursor(cur, BrowserContract.Passwords.HOSTNAME);
-    rec.httpRealm = RepoUtils.getStringFromCursor(cur, BrowserContract.Passwords.HTTP_REALM);
-    rec.formSubmitURL = RepoUtils.getStringFromCursor(cur, BrowserContract.Passwords.FORM_SUBMIT_URL);
-    rec.usernameField = RepoUtils.getStringFromCursor(cur, BrowserContract.Passwords.USERNAME_FIELD);
-    rec.passwordField = RepoUtils.getStringFromCursor(cur, BrowserContract.Passwords.PASSWORD_FIELD);
-    rec.encType = RepoUtils.getStringFromCursor(cur, BrowserContract.Passwords.ENC_TYPE);
+    rec.id = RepoUtils.getStringFromCursor(cur, PASSWORDS_ID);
+    rec.hostname = RepoUtils.getStringFromCursor(cur, PASSWORDS_HOSTNAME);
+    rec.httpRealm = RepoUtils.getStringFromCursor(cur, PASSWORDS_HTTP_REALM);
+    rec.formSubmitURL = RepoUtils.getStringFromCursor(cur, PASSWORDS_FORM_SUBMIT_URL);
+    rec.usernameField = RepoUtils.getStringFromCursor(cur, PASSWORDS_USERNAME_FIELD);
+    rec.passwordField = RepoUtils.getStringFromCursor(cur, PASSWORDS_PASSWORD_FIELD);
+    rec.encType = RepoUtils.getStringFromCursor(cur, PASSWORDS_ENC_TYPE);
 
     // TODO decryption of username/password here (Bug 711636)
-    rec.encryptedUsername = RepoUtils.getStringFromCursor(cur, BrowserContract.Passwords.ENCRYPTED_USERNAME);
-    rec.encryptedPassword = RepoUtils.getStringFromCursor(cur, BrowserContract.Passwords.ENCRYPTED_PASSWORD);
+    rec.encryptedUsername = RepoUtils.getStringFromCursor(cur, PASSWORDS_ENCRYPTED_USERNAME);
+    rec.encryptedPassword = RepoUtils.getStringFromCursor(cur, PASSWORDS_ENCRYPTED_PASSWORD);
 
-    rec.timeCreated = RepoUtils.getLongFromCursor(cur, BrowserContract.Passwords.TIME_CREATED);
-    rec.timeLastUsed = RepoUtils.getLongFromCursor(cur, BrowserContract.Passwords.TIME_LAST_USED);
-    rec.timePasswordChanged = RepoUtils.getLongFromCursor(cur, BrowserContract.Passwords.TIME_PASSWORD_CHANGED);
-    rec.timesUsed = RepoUtils.getLongFromCursor(cur, BrowserContract.Passwords.TIMES_USED);
+    rec.timeCreated = RepoUtils.getLongFromCursor(cur, PASSWORDS_TIME_CREATED);
+    rec.timeLastUsed = RepoUtils.getLongFromCursor(cur, PASSWORDS_TIME_LAST_USED);
+    rec.timePasswordChanged = RepoUtils.getLongFromCursor(cur, PASSWORDS_TIME_PASSWORD_CHANGED);
+    rec.timesUsed = RepoUtils.getLongFromCursor(cur, PASSWORDS_TIMES_USED);
     return rec;
   }
 
   private static PasswordRecord deletedPasswordRecordFromCursor(Cursor cur) {
     if (cur.isAfterLast()) {
       return null;
     }
-    String guid = RepoUtils.getStringFromCursor(cur, DeletedColumns.GUID);
-    long lastModified = RepoUtils.getLongFromCursor(cur, DeletedColumns.TIME_DELETED);
+    String guid = RepoUtils.getStringFromCursor(cur, DELETED_PASSWORDS_GUID);
+    long lastModified = RepoUtils.getLongFromCursor(cur, DELETED_PASSWORDS_TIME_DELETED);
     PasswordRecord rec = new PasswordRecord(guid, COLLECTION, lastModified, true);
-    rec.androidID = RepoUtils.getLongFromCursor(cur, DeletedColumns.ID);
+    rec.androidID = RepoUtils.getLongFromCursor(cur, DELETED_PASSWORDS_ID);
     return rec;
   }
 
   private static ContentValues getContentValues(Record record) {
     PasswordRecord rec = (PasswordRecord) record;
 
     ContentValues cv = new ContentValues();
-    cv.put(BrowserContract.Passwords.GUID,            rec.guid);
-    cv.put(BrowserContract.Passwords.HOSTNAME,        rec.hostname);
-    cv.put(BrowserContract.Passwords.HTTP_REALM,      rec.httpRealm);
-    cv.put(BrowserContract.Passwords.FORM_SUBMIT_URL, rec.formSubmitURL);
-    cv.put(BrowserContract.Passwords.USERNAME_FIELD,  rec.usernameField);
-    cv.put(BrowserContract.Passwords.PASSWORD_FIELD,  rec.passwordField);
+    cv.put(PASSWORDS_GUID,            rec.guid);
+    cv.put(PASSWORDS_HOSTNAME,        rec.hostname);
+    cv.put(PASSWORDS_HTTP_REALM,      rec.httpRealm);
+    cv.put(PASSWORDS_FORM_SUBMIT_URL, rec.formSubmitURL);
+    cv.put(PASSWORDS_USERNAME_FIELD,  rec.usernameField);
+    cv.put(PASSWORDS_PASSWORD_FIELD,  rec.passwordField);
 
-    // TODO Do encryption of username/password here. Bug 711636
-    cv.put(BrowserContract.Passwords.ENC_TYPE,           rec.encType);
-    cv.put(BrowserContract.Passwords.ENCRYPTED_USERNAME, rec.encryptedUsername);
-    cv.put(BrowserContract.Passwords.ENCRYPTED_PASSWORD, rec.encryptedPassword);
+    // Android Logins table introduces not null constraint for encType.
+    // Add encType to contentValues only iff it is not null.
+    if (!AppConstants.MOZ_ANDROID_LOGINS_STORAGE_USE || rec.encType != null) {
+      // TODO Do encryption of username/password here. Bug 711636
+      cv.put(PASSWORDS_ENC_TYPE, rec.encType);
+    }
 
-    cv.put(BrowserContract.Passwords.TIME_CREATED,          rec.timeCreated);
-    cv.put(BrowserContract.Passwords.TIME_LAST_USED,        rec.timeLastUsed);
-    cv.put(BrowserContract.Passwords.TIME_PASSWORD_CHANGED, rec.timePasswordChanged);
-    cv.put(BrowserContract.Passwords.TIMES_USED,            rec.timesUsed);
+    cv.put(PASSWORDS_ENCRYPTED_USERNAME, rec.encryptedUsername);
+    cv.put(PASSWORDS_ENCRYPTED_PASSWORD, rec.encryptedPassword);
+
+    cv.put(PASSWORDS_TIME_CREATED,          rec.timeCreated);
+    cv.put(PASSWORDS_TIME_LAST_USED,        rec.timeLastUsed);
+    cv.put(PASSWORDS_TIME_PASSWORD_CHANGED, rec.timePasswordChanged);
+    cv.put(PASSWORDS_TIMES_USED,            rec.timesUsed);
     return cv;
   }
 }
--- a/mobile/android/tests/background/junit3/src/org/mozilla/gecko/background/db/TestPasswordsRepository.java
+++ b/mobile/android/tests/background/junit3/src/org/mozilla/gecko/background/db/TestPasswordsRepository.java
@@ -1,16 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 package org.mozilla.gecko.background.db;
 
 import java.util.HashSet;
 import java.util.Set;
 
+import org.mozilla.gecko.AppConstants;
 import org.mozilla.gecko.background.helpers.AndroidSyncTestCase;
 import org.mozilla.gecko.background.sync.helpers.ExpectFetchDelegate;
 import org.mozilla.gecko.background.sync.helpers.ExpectFetchSinceDelegate;
 import org.mozilla.gecko.background.sync.helpers.ExpectGuidsSinceDelegate;
 import org.mozilla.gecko.background.sync.helpers.ExpectNoStoreDelegate;
 import org.mozilla.gecko.background.sync.helpers.ExpectStoredDelegate;
 import org.mozilla.gecko.background.sync.helpers.PasswordHelpers;
 import org.mozilla.gecko.background.sync.helpers.SessionTestHelper;
@@ -28,22 +29,45 @@ import org.mozilla.gecko.sync.repositori
 import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionStoreDelegate;
 import org.mozilla.gecko.sync.repositories.domain.PasswordRecord;
 import org.mozilla.gecko.sync.repositories.domain.Record;
 
 import android.content.ContentProviderClient;
 import android.content.ContentValues;
 import android.content.Context;
 import android.database.Cursor;
+import android.net.Uri;
 import android.os.RemoteException;
 
 public class TestPasswordsRepository extends AndroidSyncTestCase {
+  private static final String DELETED_PASSWORD_GUID;
+  private static final String DELETED_PASSWORD_TIME_DELETED;
+  private static final String DELETED_PASSWORD_ID;
+  private static final Uri AUTHORITY_URI;
+  private static final String PASSWORDS_GUID;
+
   private final String NEW_PASSWORD1 = "password";
   private final String NEW_PASSWORD2 = "drowssap";
 
+  static {
+    if (AppConstants.MOZ_ANDROID_LOGINS_STORAGE_USE) {
+      PASSWORDS_GUID = BrowserContract.Logins.GUID;
+      DELETED_PASSWORD_GUID = BrowserContract.DeletedLogins.GUID;
+      DELETED_PASSWORD_TIME_DELETED = BrowserContract.DeletedLogins.TIME_DELETED;
+      DELETED_PASSWORD_ID = BrowserContract.DeletedLogins._ID;
+      AUTHORITY_URI = BrowserContract.LOGINS_AUTHORITY_URI;
+    } else {
+      PASSWORDS_GUID = BrowserContract.Passwords.GUID;
+      DELETED_PASSWORD_GUID = BrowserContract.DeletedColumns.GUID;
+      DELETED_PASSWORD_TIME_DELETED = BrowserContract.DeletedColumns.TIME_DELETED;
+      DELETED_PASSWORD_ID = BrowserContract.DeletedColumns.ID;
+      AUTHORITY_URI = BrowserContract.PASSWORDS_AUTHORITY_URI;
+    }
+  }
+
   @Override
   public void setUp() {
     wipe();
     assertTrue(WaitHelper.getTestWaiter().isIdle());
   }
 
   public void testFetchAll() {
     RepositorySession session = createAndBeginSession();
@@ -94,20 +118,20 @@ public class TestPasswordsRepository ext
     long timeModified1 = updatePassword(NEW_PASSWORD1, record1);
     performWait(storeRunnable(session, record1));
 
     PasswordRecord record2 = PasswordHelpers.createPassword2();
     long timeModified2 = updatePassword(NEW_PASSWORD2, record2);
     performWait(storeRunnable(session, record2));
 
     String[] expectedOne = new String[] { record2.guid };
-    performWait(fetchSinceRunnable(session, timeModified2 - 10, expectedOne));
+    performWait(fetchSinceRunnable(session, timeModified2 - 1, expectedOne));
 
     String[] expectedBoth = new String[] { record1.guid, record2.guid };
-    performWait(fetchSinceRunnable(session, timeModified1 - 10, expectedBoth));
+    performWait(fetchSinceRunnable(session, timeModified1 - 1, expectedBoth));
 
     dispose(session);
   }
 
   public void testFetchSinceReturnNoRecords() {
    RepositorySession session = createAndBeginSession();
 
     performWait(storeRunnable(session, PasswordHelpers.createPassword2()));
@@ -337,23 +361,23 @@ public class TestPasswordsRepository ext
   public void testRawFetch() throws RemoteException {
     RepositorySession session = createAndBeginSession();
     Record[] expected = new Record[] { PasswordHelpers.createPassword1(),
                                        PasswordHelpers.createPassword2() };
 
     performWait(storeRunnable(session, expected[0]));
     performWait(storeRunnable(session, expected[1]));
 
-    ContentProviderClient client = getApplicationContext().getContentResolver().acquireContentProviderClient(BrowserContract.PASSWORDS_AUTHORITY_URI);
+    ContentProviderClient client = getApplicationContext().getContentResolver().acquireContentProviderClient(AUTHORITY_URI);
     Cursor cursor = client.query(BrowserContractHelpers.PASSWORDS_CONTENT_URI, null, null, null, null);
     assertEquals(2, cursor.getCount());
     cursor.moveToFirst();
     Set<String> guids = new HashSet<String>();
     while (!cursor.isAfterLast()) {
-      String guid = RepoUtils.getStringFromCursor(cursor, BrowserContract.Passwords.GUID);
+      String guid = RepoUtils.getStringFromCursor(cursor, PASSWORDS_GUID);
       guids.add(guid);
       cursor.moveToNext();
     }
     cursor.close();
     assertEquals(2, guids.size());
     assertTrue(guids.contains(expected[0].guid));
     assertTrue(guids.contains(expected[1].guid));
     dispose(session);
@@ -392,19 +416,19 @@ public class TestPasswordsRepository ext
     context.getContentResolver().delete(BrowserContractHelpers.DELETED_PASSWORDS_CONTENT_URI, null, null);
   }
 
   private void storeLocalDeletedRecord(Record record, long time) {
     // Wipe data-store
     wipe();
     // Store record in deleted table.
     ContentValues contentValues = new ContentValues();
-    contentValues.put(BrowserContract.DeletedColumns.GUID, record.guid);
-    contentValues.put(BrowserContract.DeletedColumns.TIME_DELETED, time);
-    contentValues.put(BrowserContract.DeletedColumns.ID, record.androidID);
+    contentValues.put(DELETED_PASSWORD_GUID, record.guid);
+    contentValues.put(DELETED_PASSWORD_TIME_DELETED, time);
+    contentValues.put(DELETED_PASSWORD_ID, record.androidID);
     getApplicationContext().getContentResolver().insert(BrowserContractHelpers.DELETED_PASSWORDS_CONTENT_URI, contentValues);
   }
 
   private static void dispose(RepositorySession session) {
     if (session != null) {
       session.abort();
     }
   }
--- a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testPasswordEncrypt.java
+++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testPasswordEncrypt.java
@@ -2,27 +2,34 @@
  * 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.tests;
 
 import java.io.File;
 
 import org.json.JSONObject;
+import org.mozilla.gecko.AppConstants;
 import org.mozilla.gecko.NSSBridge;
 import org.mozilla.gecko.db.BrowserContract;
 
 import android.content.ContentResolver;
 import android.content.ContentValues;
 import android.content.Context;
 import android.database.Cursor;
 import android.net.Uri;
+import android.util.Log;
 
 public class testPasswordEncrypt extends BaseTest {
     public void testPasswordEncrypt() {
+        if (AppConstants.MOZ_ANDROID_LOGINS_STORAGE_USE) {
+            Log.d(LOGTAG, "New LoginsProvider in use. Skipping old PasswordsProvider tests");
+            return;
+        }
+
         Context context = (Context)getActivity();
         ContentResolver cr = context.getContentResolver();
         mAsserter.isnot(cr, null, "Found a content resolver");
         ContentValues cvs = new ContentValues();
 
         blockForGeckoReady();
 
         File db = new File(mProfile, "signons.sqlite");
--- a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testPasswordProvider.java
+++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testPasswordProvider.java
@@ -1,39 +1,46 @@
 /* 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.tests;
 
 import java.io.File;
 
+import org.mozilla.gecko.AppConstants;
 import org.mozilla.gecko.db.BrowserContract;
 import org.mozilla.gecko.db.BrowserContract.GeckoDisabledHosts;
 import org.mozilla.gecko.db.BrowserContract.Passwords;
 
 import android.content.ContentResolver;
 import android.content.ContentValues;
 import android.content.Context;
 import android.database.Cursor;
 import android.net.Uri;
+import android.util.Log;
 
 /**
  * A basic password contentprovider test.
  * - inserts a password when the database is not yet set up
  * - inserts a password
  * - updates a password
  * - deletes a password
  * - inserts a disabled host
  * - queries for disabled host
  */
 public class testPasswordProvider extends BaseTest {
     private static final String DB_NAME = "signons.sqlite";
 
     public void testPasswordProvider() {
+        if (AppConstants.MOZ_ANDROID_LOGINS_STORAGE_USE) {
+            Log.d(LOGTAG, "New LoginsProvider in use. Skipping old PasswordsProvider tests");
+            return;
+        }
+
         Context context = (Context)getActivity();
         ContentResolver cr = context.getContentResolver();
         ContentValues[] cvs = new ContentValues[1];
         cvs[0] = new ContentValues();
   
         blockForGeckoReady();
   
         cvs[0].put("hostname", "http://www.example.com");