Merge "Fix VoicemailProviderTest for new columns" into nyc-mr2-dev
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index b5bcf5e..fde3a2f 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -18,7 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="sharedUserLabel" msgid="8024311725474286801">"Android-kerneapplikationer"</string>
     <string name="app_label" msgid="3389954322874982620">"Lagring af kontakter"</string>
-    <string name="provider_label" msgid="6012150850819899907">"Kontakter"</string>
+    <string name="provider_label" msgid="6012150850819899907">"Kontaktpersoner"</string>
     <string name="upgrade_out_of_memory_notification_ticker" msgid="7638747231223520477">"Opgradering af kontaktpersoner kræver mere hukommelse."</string>
     <string name="upgrade_out_of_memory_notification_title" msgid="8888171924684998531">"Opgraderer lagring af kontaktpersoner"</string>
     <string name="upgrade_out_of_memory_notification_text" msgid="2581831842693151968">"Tryk for at gennemføre opgraderingen."</string>
diff --git a/res/values-kn-rIN/strings.xml b/res/values-kn-rIN/strings.xml
index 17447bf..0e37eae 100644
--- a/res/values-kn-rIN/strings.xml
+++ b/res/values-kn-rIN/strings.xml
@@ -27,7 +27,7 @@
     <string name="voicemail_from_column" msgid="435732568832121444">"ಇದರಿಂದ ಧ್ವನಿಮೇಲ್‌ "</string>
     <string name="debug_dump_title" msgid="4916885724165570279">"ಸಂಪರ್ಕಗಳ ಡೇಟಾಬೇಸ್‌‌ ನಕಲಿಸಿ"</string>
     <string name="debug_dump_database_message" msgid="406438635002392290">"ನೀವು 1) ಎಲ್ಲಾ ಸಂಪರ್ಕಗಳ ಸಂಬಂಧಿಸಿದ ಮಾಹಿತಿಯನ್ನು ಒಳಗೊಂಡಿರುವ ನಿಮ್ಮ ಡೇಟಾಬೇಸ್ ನಕಲು ಮಾಡಲು ಮತ್ತು ಆಂತರಿಕ ಸಂಗ್ರಹಣೆಗೆ ಎಲ್ಲ ಕರೆಯ ಲಾಗ್‌ ಮಾಡಲು ಮತ್ತು 2) ಇಮೇಲ್‌‌ ಮಾಡಲಿರುವಿರಿ. ನೀವು ಸಾಧನವನ್ನು ಯಶಸ್ವಿಯಾಗಿ ನಕಲು ಮಾಡಿದ ಬಳಿಕ ಅಥವಾ ಇಮೇಲ್‌ ಸ್ವೀಕರಿಸಿದ ಕೂಡಲೇ ನಕಲು ಅಳಿಸುವುದನ್ನು ನೆನಪಿನಲ್ಲಿರಿಸಿಕೊಳ್ಳಿ."</string>
-    <string name="debug_dump_delete_button" msgid="7832879421132026435">"ಈಗ ಅಳಿಸು"</string>
+    <string name="debug_dump_delete_button" msgid="7832879421132026435">"ಈಗ ಅಳಿಸಿ"</string>
     <string name="debug_dump_start_button" msgid="2837506913757600001">"ಪ್ರಾರಂಭಿಸು"</string>
     <string name="debug_dump_email_sender_picker" msgid="3534420908672176460">"ನಿಮ್ಮ ಫೈಲ್ ಕಳುಹಿಸಲು ಪ್ರೋಗ್ರಾಂ ಒಂದನ್ನು ಆಯ್ಕೆ ಮಾಡಿ"</string>
     <string name="debug_dump_email_subject" msgid="108188398416385976">"ಸಂಪರ್ಕಗಳ Db ಲಗತ್ತಿಸಲಾಗಿದೆ"</string>
diff --git a/src/com/android/providers/contacts/CallLogDatabaseHelper.java b/src/com/android/providers/contacts/CallLogDatabaseHelper.java
index 67c548a..c88b742 100644
--- a/src/com/android/providers/contacts/CallLogDatabaseHelper.java
+++ b/src/com/android/providers/contacts/CallLogDatabaseHelper.java
@@ -37,7 +37,7 @@
 public class CallLogDatabaseHelper {
     private static final String TAG = "CallLogDatabaseHelper";
 
-    private static final int DATABASE_VERSION = 3;
+    private static final int DATABASE_VERSION = 4;
 
     private static final boolean DEBUG = false; // DON'T SUBMIT WITH TRUE
 
@@ -151,7 +151,11 @@
                     Voicemails.TRANSCRIPTION + " TEXT," +
                     Voicemails.STATE + " INTEGER," +
                     Voicemails.DIRTY + " INTEGER NOT NULL DEFAULT 0," +
-                    Voicemails.DELETED + " INTEGER NOT NULL DEFAULT 0" +
+                    Voicemails.DELETED + " INTEGER NOT NULL DEFAULT 0," +
+                    Voicemails.BACKED_UP + " INTEGER NOT NULL DEFAULT 0," +
+                    Voicemails.RESTORED + " INTEGER NOT NULL DEFAULT 0," +
+                    Voicemails.ARCHIVED + " INTEGER NOT NULL DEFAULT 0," +
+                    Voicemails.IS_OMTP_VOICEMAIL + " INTEGER NOT NULL DEFAULT 0" +
                     ");");
 
             db.execSQL("CREATE TABLE " + Tables.VOICEMAIL_STATUS + " (" +
@@ -185,6 +189,10 @@
             if (oldVersion < 3) {
                 upgradeToVersion3(db);
             }
+
+            if (oldVersion < 4) {
+                upgradeToVersion4(db);
+            }
         }
     }
 
@@ -243,6 +251,17 @@
     }
 
     /**
+     * Add {@link Voicemails.BACKED_UP} {@link Voicemails.ARCHIVE} {@link
+     * Voicemails.IS_OMTP_VOICEMAIL} column to the CallLog database.
+     */
+    private void upgradeToVersion4(SQLiteDatabase db) {
+        db.execSQL("ALTER TABLE calls ADD backed_up INTEGER NOT NULL DEFAULT 0");
+        db.execSQL("ALTER TABLE calls ADD restored INTEGER NOT NULL DEFAULT 0");
+        db.execSQL("ALTER TABLE calls ADD archived INTEGER NOT NULL DEFAULT 0");
+        db.execSQL("ALTER TABLE calls ADD is_omtp_voicemail INTEGER NOT NULL DEFAULT 0");
+    }
+
+    /**
      * Perform the migration from the contacts2.db (of the latest version) to the current calllog/
      * voicemail status tables.
      */
diff --git a/src/com/android/providers/contacts/CallLogProvider.java b/src/com/android/providers/contacts/CallLogProvider.java
index a3bed8c..9a5b7c4 100644
--- a/src/com/android/providers/contacts/CallLogProvider.java
+++ b/src/com/android/providers/contacts/CallLogProvider.java
@@ -46,11 +46,11 @@
 import android.telecom.TelecomManager;
 import android.text.TextUtils;
 import android.util.Log;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.providers.contacts.CallLogDatabaseHelper.DbProperties;
 import com.android.providers.contacts.CallLogDatabaseHelper.Tables;
 import com.android.providers.contacts.util.SelectionBuilder;
 import com.android.providers.contacts.util.UserUtils;
-import com.google.common.annotations.VisibleForTesting;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
@@ -153,6 +153,19 @@
         sCallsProjectionMap.put(Calls.LAST_MODIFIED, Calls.LAST_MODIFIED);
     }
 
+    private static final String ALLOWED_PACKAGE_FOR_TESTING = "com.android.providers.contacts";
+
+    @VisibleForTesting
+    static final String PARAM_KEY_QUERY_FOR_TESTING = "query_for_testing";
+
+    /**
+     * A long to override the clock used for timestamps, or "null" to reset to the system clock.
+     */
+    @VisibleForTesting
+    static final String PARAM_KEY_SET_TIME_FOR_TESTING = "set_time_for_testing";
+
+    private static Long sTimeForTestMillis;
+
     private HandlerThread mBackgroundThread;
     private Handler mBackgroundHandler;
     private volatile CountDownLatch mReadAccessLatch;
@@ -223,6 +236,9 @@
                     "  order=[" + sortOrder + "] CPID=" + Binder.getCallingPid() +
                     " User=" + UserUtils.getCurrentUserHandle(getContext()));
         }
+
+        queryForTesting(uri);
+
         waitForAccess(mReadAccessLatch);
         final SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
         qb.setTables(Tables.CALLS);
@@ -278,6 +294,30 @@
         return c;
     }
 
+    private void queryForTesting(Uri uri) {
+        if (!uri.getBooleanQueryParameter(PARAM_KEY_QUERY_FOR_TESTING, false)) {
+            return;
+        }
+        if (!getCallingPackage().equals(ALLOWED_PACKAGE_FOR_TESTING)) {
+            throw new IllegalArgumentException("query_for_testing set from foreign package "
+                    + getCallingPackage());
+        }
+
+        String timeString = uri.getQueryParameter(PARAM_KEY_SET_TIME_FOR_TESTING);
+        if (timeString != null) {
+            if (timeString.equals("null")) {
+                sTimeForTestMillis = null;
+            } else {
+                sTimeForTestMillis = Long.parseLong(timeString);
+            }
+        }
+    }
+
+    @VisibleForTesting
+    static Long getTimeForTestMillis() {
+        return sTimeForTestMillis;
+    }
+
     /**
      * Gets an integer query parameter from a given uri.
      *
diff --git a/src/com/android/providers/contacts/DbModifierWithNotification.java b/src/com/android/providers/contacts/DbModifierWithNotification.java
index ef1b847..b3cf0ef 100644
--- a/src/com/android/providers/contacts/DbModifierWithNotification.java
+++ b/src/com/android/providers/contacts/DbModifierWithNotification.java
@@ -63,7 +63,8 @@
     private static final int SOURCE_PACKAGE_COLUMN_INDEX = 0;
     private static final String NON_NULL_SOURCE_PACKAGE_SELECTION =
             VoicemailContract.SOURCE_PACKAGE_FIELD + " IS NOT NULL";
-
+    private static final String NOT_DELETED_SELECTION =
+            Voicemails.DELETED + " == 0";
     private final String mTableName;
     private final SQLiteDatabase mDb;
     private final InsertHelper mInsertHelper;
@@ -98,7 +99,7 @@
     public long insert(String table, String nullColumnHack, ContentValues values) {
         Set<String> packagesModified = getModifiedPackages(values);
         if (mIsCallsTable) {
-            values.put(Calls.LAST_MODIFIED, System.currentTimeMillis());
+            values.put(Calls.LAST_MODIFIED, getTimeMillis());
         }
         long rowId = mDb.insert(table, nullColumnHack, values);
         if (rowId > 0 && packagesModified.size() != 0) {
@@ -115,7 +116,7 @@
     public long insert(ContentValues values) {
         Set<String> packagesModified = getModifiedPackages(values);
         if (mIsCallsTable) {
-            values.put(Calls.LAST_MODIFIED, System.currentTimeMillis());
+            values.put(Calls.LAST_MODIFIED, getTimeMillis());
         }
         long rowId = mInsertHelper.insert(values);
         if (rowId > 0 && packagesModified.size() != 0) {
@@ -160,8 +161,12 @@
 
         boolean hasMarkedRead = false;
         if (mIsCallsTable) {
-            values.put(Calls.LAST_MODIFIED, System.currentTimeMillis());
-
+            if (values.containsKey(Voicemails.DELETED)
+                    && !values.getAsBoolean(Voicemails.DELETED)) {
+                values.put(Calls.LAST_MODIFIED, getTimeMillis());
+            } else {
+                updateLastModified(table, whereClause, whereArgs);
+            }
             if (isVoicemail) {
                 // If a calling package is modifying its own entries, it means that the change came
                 // from the server and thus is synced or "clean". Otherwise, it means that a local
@@ -199,6 +204,15 @@
         return count;
     }
 
+    private void updateLastModified(String table, String whereClause, String[] whereArgs) {
+        ContentValues values = new ContentValues();
+        values.put(Calls.LAST_MODIFIED, getTimeMillis());
+
+        mDb.update(table, values,
+                DbQueryUtils.concatenateClauses(NOT_DELETED_SELECTION, whereClause),
+                whereArgs);
+    }
+
     @Override
     public int delete(String table, String whereClause, String[] whereArgs) {
         Set<String> packagesModified = getModifiedPackages(whereClause, whereArgs);
@@ -217,7 +231,7 @@
             ContentValues values = new ContentValues();
             values.put(VoicemailContract.Voicemails.DIRTY, 1);
             values.put(VoicemailContract.Voicemails.DELETED, 1);
-            values.put(VoicemailContract.Voicemails.LAST_MODIFIED, System.currentTimeMillis());
+            values.put(VoicemailContract.Voicemails.LAST_MODIFIED, getTimeMillis());
             count = mDb.update(table, values, whereClause, whereArgs);
         } else {
             count = mDb.delete(table, whereClause, whereArgs);
@@ -363,4 +377,11 @@
         }
         return values.getAsBoolean(key);
     }
+
+    private long getTimeMillis() {
+        if (CallLogProvider.getTimeForTestMillis() == null) {
+            return System.currentTimeMillis();
+        }
+        return CallLogProvider.getTimeForTestMillis();
+    }
 }
diff --git a/src/com/android/providers/contacts/VoicemailContentTable.java b/src/com/android/providers/contacts/VoicemailContentTable.java
index 24a02bc..75f9574 100644
--- a/src/com/android/providers/contacts/VoicemailContentTable.java
+++ b/src/com/android/providers/contacts/VoicemailContentTable.java
@@ -70,6 +70,10 @@
             .add(Voicemails.DIRTY)
             .add(Voicemails.DELETED)
             .add(Voicemails.LAST_MODIFIED)
+            .add(Voicemails.BACKED_UP)
+            .add(Voicemails.RESTORED)
+            .add(Voicemails.ARCHIVED)
+            .add(Voicemails.IS_OMTP_VOICEMAIL)
             .add(OpenableColumns.DISPLAY_NAME)
             .add(OpenableColumns.SIZE)
             .build();
@@ -105,6 +109,10 @@
                 .add(Voicemails.DIRTY)
                 .add(Voicemails.DELETED)
                 .add(Voicemails.LAST_MODIFIED)
+                .add(Voicemails.BACKED_UP)
+                .add(Voicemails.RESTORED)
+                .add(Voicemails.ARCHIVED)
+                .add(Voicemails.IS_OMTP_VOICEMAIL)
                 .add(OpenableColumns.DISPLAY_NAME, createDisplayName(context))
                 .add(OpenableColumns.SIZE, "NULL")
                 .build();
diff --git a/tests/src/com/android/providers/contacts/BaseContactsProvider2Test.java b/tests/src/com/android/providers/contacts/BaseContactsProvider2Test.java
index 86dff66..0924154 100644
--- a/tests/src/com/android/providers/contacts/BaseContactsProvider2Test.java
+++ b/tests/src/com/android/providers/contacts/BaseContactsProvider2Test.java
@@ -31,6 +31,7 @@
 import android.net.Uri;
 import android.provider.BaseColumns;
 import android.provider.CallLog;
+import android.provider.CallLog.Calls;
 import android.provider.ContactsContract;
 import android.provider.ContactsContract.AggregationExceptions;
 import android.provider.ContactsContract.CommonDataKinds.Email;
@@ -1355,16 +1356,21 @@
         }
     }
 
-    protected void assertLastModified(Uri uri) {
-        assertLastModified(uri, System.currentTimeMillis(), 1000);
-    }
-
-    protected void assertLastModified(Uri uri, long time, long tolerance) {
+    protected void assertLastModified(Uri uri, long time) {
         Cursor c = mResolver.query(uri, null, null, null, null);
         c.moveToFirst();
         int index = c.getColumnIndex(CallLog.Calls.LAST_MODIFIED);
         long timeStamp = c.getLong(index);
-        assertTrue(Math.abs(time - timeStamp) < tolerance);
+        assertEquals(timeStamp, time);
+    }
+
+    protected void setTimeForTest(Long time) {
+        Uri uri = Calls.CONTENT_URI.buildUpon()
+                .appendQueryParameter(CallLogProvider.PARAM_KEY_QUERY_FOR_TESTING, "1")
+                .appendQueryParameter(CallLogProvider.PARAM_KEY_SET_TIME_FOR_TESTING,
+                        time == null ? "null" : time.toString())
+                .build();
+        mResolver.query(uri, null, null, null, null);
     }
     /**
      * A contact in the database, and the attributes used to create it.  Construct using
diff --git a/tests/src/com/android/providers/contacts/CallLogProviderTest.java b/tests/src/com/android/providers/contacts/CallLogProviderTest.java
index f492e73..8ee7a5b 100644
--- a/tests/src/com/android/providers/contacts/CallLogProviderTest.java
+++ b/tests/src/com/android/providers/contacts/CallLogProviderTest.java
@@ -85,16 +85,18 @@
     protected void tearDown() throws Exception {
         setUpWithVoicemailPermissions();
         mResolver.delete(Calls.CONTENT_URI_WITH_VOICEMAIL, null, null);
+        setTimeForTest(null);
         super.tearDown();
     }
 
     public void testInsert_RegularCallRecord() {
+        setTimeForTest(1000L);
         ContentValues values = getDefaultCallValues();
         Uri uri = mResolver.insert(Calls.CONTENT_URI, values);
         values.put(Calls.COUNTRY_ISO, "us");
         assertStoredValues(uri, values);
         assertSelection(uri, values, Calls._ID, ContentUris.parseId(uri));
-        assertLastModified(uri);
+        assertLastModified(uri, 1000);
     }
 
     private void setUpWithVoicemailPermissions() {
@@ -105,6 +107,7 @@
 
     public void testInsert_VoicemailCallRecord() {
         setUpWithVoicemailPermissions();
+        setTimeForTest(1000L);
         final ContentValues values = getDefaultCallValues();
         values.put(Calls.TYPE, Calls.VOICEMAIL_TYPE);
         values.put(Calls.VOICEMAIL_URI, "content://foo/voicemail/2");
@@ -121,10 +124,11 @@
         Uri uri  = mResolver.insert(Calls.CONTENT_URI_WITH_VOICEMAIL, values);
         assertStoredValues(uri, values);
         assertSelection(uri, values, Calls._ID, ContentUris.parseId(uri));
-        assertLastModified(uri);
+        assertLastModified(uri, 1000);
     }
 
     public void testUpdate() {
+        setTimeForTest(1000L);
         Uri uri = insertCallRecord();
         ContentValues values = new ContentValues();
         values.put(Calls.TYPE, Calls.OUTGOING_TYPE);
@@ -139,7 +143,7 @@
         int count = mResolver.update(uri, values, null, null);
         assertEquals(1, count);
         assertStoredValues(uri, values);
-        assertLastModified(uri);
+        assertLastModified(uri, 1000);
     }
 
     public void testDelete() {
diff --git a/tests/src/com/android/providers/contacts/VoicemailProviderTest.java b/tests/src/com/android/providers/contacts/VoicemailProviderTest.java
index 05491ab..16abf2f 100644
--- a/tests/src/com/android/providers/contacts/VoicemailProviderTest.java
+++ b/tests/src/com/android/providers/contacts/VoicemailProviderTest.java
@@ -20,8 +20,10 @@
 import android.content.ContentValues;
 import android.database.Cursor;
 import android.net.Uri;
+import android.os.BatteryStats.Uid.Proc;
 import android.os.ParcelFileDescriptor;
 import android.os.Process;
+import android.provider.CallLog;
 import android.provider.CallLog.Calls;
 import android.provider.VoicemailContract;
 import android.provider.VoicemailContract.Status;
@@ -65,6 +67,7 @@
     protected void setUp() throws Exception {
         super.setUp();
         setUpForOwnPermission();
+        addProvider(CallLogProviderTestable.class, CallLog.AUTHORITY);
     }
 
     /** Returns the appropriate /voicemail URI. */
@@ -80,6 +83,7 @@
     }
 
     public void testInsert() throws Exception {
+        setTimeForTest(1000L);
         Uri uri = mResolver.insert(voicemailUri(), getTestVoicemailValues());
         // We create on purpose a new set of ContentValues here, because the code above modifies
         // the copy it gets.
@@ -87,7 +91,7 @@
         assertSelection(uri, getTestVoicemailValues(), Voicemails._ID, ContentUris.parseId(uri));
         assertEquals(1, countFilesInTestDirectory());
 
-        assertLastModified(uri);
+        assertLastModified(uri, 1000);
     }
 
     public void testInsertReadMessageIsNotNew() throws Exception {
@@ -133,6 +137,7 @@
     }
 
     public void testUpdate() {
+        setTimeForTest(1000L);
         Uri uri = insertVoicemail();
         ContentValues values = new ContentValues();
         values.put(Voicemails.NUMBER, "1-800-263-7643");
@@ -144,10 +149,14 @@
         values.put(Voicemails.SOURCE_DATA, "foo");
         values.put(Voicemails.PHONE_ACCOUNT_COMPONENT_NAME, "dummy_name");
         values.put(Voicemails.PHONE_ACCOUNT_ID, "dummy_account");
+        values.put(Voicemails.BACKED_UP, 1);
+        values.put(Voicemails.RESTORED, 1);
+        values.put(Voicemails.ARCHIVED, 1);
+        values.put(Voicemails.IS_OMTP_VOICEMAIL, 1);
         int count = mResolver.update(uri, values, null, null);
         assertEquals(1, count);
         assertStoredValues(uri, values);
-        assertLastModified(uri);
+        assertLastModified(uri, 1000);
     }
 
     public void testUpdateOwnPackageVoicemail_NotDirty() {
@@ -184,6 +193,7 @@
 
     public void testDeleteOtherPackageVoicemail_SetsDirtyStatus() {
         setUpForFullPermission();
+        setTimeForTest(1000L);
         final Uri anotherVoicemail = insertVoicemailForSourcePackage("another-package");
         assertEquals(1, getCount(voicemailUri(), null, null));
 
@@ -197,7 +207,7 @@
 
         assertEquals(1, getCount(anotherVoicemail, null, null));
         assertStoredValues(anotherVoicemail, values);
-        assertLastModified(anotherVoicemail);
+        assertLastModified(anotherVoicemail, 1000);
     }
 
     public void testDelete() {
@@ -208,6 +218,29 @@
         assertEquals(0, getCount(uri, null, null));
     }
 
+    public void testUpdateAfterDelete_lastModifiedNotChanged() {
+        setUpForFullPermission();
+        setTimeForTest(1000L);
+        final Uri anotherVoicemail = insertVoicemailForSourcePackage("another-package");
+        assertEquals(1, getCount(voicemailUri(), null, null));
+
+        // Clear the mapping for our own UID so that this doesn't look like an internal transaction.
+        mPackageManager.removePackage(Process.myUid());
+        mResolver.delete(anotherVoicemail, null, null);
+        assertLastModified(anotherVoicemail, 1000);
+
+        mPackageManager.addPackage(Process.myUid(), mActor.packageName);
+        setTimeForTest(2000L);
+        mResolver.update(anotherVoicemail, new ContentValues(), null, null);
+        assertLastModified(anotherVoicemail, 1000);
+
+        setTimeForTest(3000L);
+        ContentValues values = new ContentValues();
+        values.put(Voicemails.DELETED, "0");
+        mResolver.update(anotherVoicemail, values, null, null);
+        assertLastModified(anotherVoicemail, 3000);
+    }
+
     public void testGetType_ItemUri() throws Exception {
         // Random item uri.
         assertEquals(Voicemails.ITEM_TYPE,
@@ -735,6 +768,10 @@
         values.put(Voicemails.HAS_CONTENT, 0);
         values.put(Voicemails.SOURCE_DATA, "1234");
         values.put(Voicemails.STATE, Voicemails.STATE_INBOX);
+        values.put(Voicemails.BACKED_UP, 0);
+        values.put(Voicemails.RESTORED, 0);
+        values.put(Voicemails.ARCHIVED, 0);
+        values.put(Voicemails.IS_OMTP_VOICEMAIL, 0);
         return values;
     }