Merge remote-tracking branch 'goog/mirror-m-wireless-internal-release'

Change-Id: I439330f8c022ce85005d84fd4286f4f1ffdced1c
diff --git a/src/com/android/providers/contacts/ContactsDatabaseHelper.java b/src/com/android/providers/contacts/ContactsDatabaseHelper.java
index eb4172c..7cdda65 100644
--- a/src/com/android/providers/contacts/ContactsDatabaseHelper.java
+++ b/src/com/android/providers/contacts/ContactsDatabaseHelper.java
@@ -16,6 +16,7 @@
 
 package com.android.providers.contacts;
 
+import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.ContentValues;
 import android.content.Context;
@@ -117,7 +118,7 @@
      *   700-799 Jelly Bean
      *   800-899 Kitkat
      *   900-999 Lollipop
-     *   1000-1100 M
+     *   1000-1099 M
      * </pre>
      */
     static final int DATABASE_VERSION = 1005;
@@ -1540,13 +1541,17 @@
                 Voicemails.SOURCE_DATA + " TEXT," +
                 Voicemails.SOURCE_PACKAGE + " TEXT," +
                 Voicemails.TRANSCRIPTION + " TEXT," +
-                Voicemails.STATE + " INTEGER" +
+                Voicemails.STATE + " INTEGER," +
+                Voicemails.DIRTY + " INTEGER NOT NULL DEFAULT 0," +
+                Voicemails.DELETED + " INTEGER NOT NULL DEFAULT 0" +
         ");");
 
         // Voicemail source status table.
         db.execSQL("CREATE TABLE " + Tables.VOICEMAIL_STATUS + " (" +
                 VoicemailContract.Status._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," +
                 VoicemailContract.Status.SOURCE_PACKAGE + " TEXT UNIQUE NOT NULL," +
+                VoicemailContract.Status.PHONE_ACCOUNT_COMPONENT_NAME + " TEXT," +
+                VoicemailContract.Status.PHONE_ACCOUNT_ID + " TEXT," +
                 VoicemailContract.Status.SETTINGS_URI + " TEXT," +
                 VoicemailContract.Status.VOICEMAIL_ACCESS_URI + " TEXT," +
                 VoicemailContract.Status.CONFIGURATION_STATE + " INTEGER," +
@@ -2892,6 +2897,16 @@
             oldVersion = 1005;
         }
 
+        if (oldVersion < 1000) {
+            upgradeToVersion1000(db);
+            oldVersion = 1000;
+        }
+
+        if (oldVersion < 1001) {
+            upgradeToVersion1001(db);
+            oldVersion = 1001;
+        }
+
         if (upgradeViewsAndTriggers) {
             createContactsViews(db);
             createGroupsView(db);
@@ -4395,6 +4410,13 @@
 
     public void upgradeToVersion1005(SQLiteDatabase db) {
         db.execSQL("ALTER TABLE calls ADD photo_uri TEXT;");
+        // Add multi-sim fields
+        db.execSQL("ALTER TABLE voicemail_status ADD phone_account_component_name TEXT;");
+        db.execSQL("ALTER TABLE voicemail_status ADD phone_account_id TEXT;");
+
+        // For use by the sync adapter
+        db.execSQL("ALTER TABLE calls ADD dirty INTEGER NOT NULL DEFAULT 0;");
+        db.execSQL("ALTER TABLE calls ADD deleted INTEGER NOT NULL DEFAULT 0;");
     }
 
     public String extractHandleFromEmailAddress(String email) {
diff --git a/src/com/android/providers/contacts/DbModifierWithNotification.java b/src/com/android/providers/contacts/DbModifierWithNotification.java
index 4830999..3576849 100644
--- a/src/com/android/providers/contacts/DbModifierWithNotification.java
+++ b/src/com/android/providers/contacts/DbModifierWithNotification.java
@@ -21,6 +21,7 @@
 import static android.Manifest.permission.READ_VOICEMAIL;
 
 import android.content.ComponentName;
+import android.content.ContentResolver;
 import android.content.ContentUris;
 import android.content.ContentValues;
 import android.content.Context;
@@ -32,6 +33,7 @@
 import android.database.sqlite.SQLiteDatabase;
 import android.net.Uri;
 import android.os.Binder;
+import android.os.Bundle;
 import android.provider.CallLog.Calls;
 import android.provider.VoicemailContract;
 import android.provider.VoicemailContract.Status;
@@ -42,6 +44,7 @@
 import com.android.providers.contacts.ContactsDatabaseHelper.Tables;
 import com.android.providers.contacts.util.DbQueryUtils;
 import com.google.android.collect.Lists;
+import com.google.common.collect.Iterables;
 
 import java.util.ArrayList;
 import java.util.Collection;
@@ -149,8 +152,20 @@
     public int update(String table, ContentValues values, String whereClause, String[] whereArgs) {
         Set<String> packagesModified = getModifiedPackages(whereClause, whereArgs);
         packagesModified.addAll(getModifiedPackages(values));
+
+        boolean isVoicemail = packagesModified.size() != 0;
+
+        if (mIsCallsTable && 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 change
+            // is being made to the database, so the entries should be marked as "dirty" so that
+            // the corresponding sync adapter knows they need to be synced.
+            final int isDirty = isSelfModifying(packagesModified) ? 0 : 1;
+            values.put(VoicemailContract.Voicemails.DIRTY, isDirty);
+        }
+
         int count = mDb.update(table, values, whereClause, whereArgs);
-        if (count > 0 && packagesModified.size() != 0) {
+        if (count > 0 && isVoicemail) {
             notifyVoicemailChange(mBaseUri, packagesModified, Intent.ACTION_PROVIDER_CHANGED);
         }
         if (count > 0 && mIsCallsTable) {
@@ -162,8 +177,25 @@
     @Override
     public int delete(String table, String whereClause, String[] whereArgs) {
         Set<String> packagesModified = getModifiedPackages(whereClause, whereArgs);
-        int count = mDb.delete(table, whereClause, whereArgs);
-        if (count > 0 && packagesModified.size() != 0) {
+        boolean isVoicemail = packagesModified.size() != 0;
+
+        // If a deletion is made by a package that is not the package that inserted the voicemail,
+        // this means that the user deleted the voicemail. However, we do not want to delete it from
+        // the database until after the server has been notified of the deletion. To ensure this,
+        // mark the entry as "deleted"--deleted entries should be hidden from the user.
+        // Once the changes are synced to the server, delete will be called again, this time
+        // removing the rows from the table.
+        final int count;
+        if (mIsCallsTable && isVoicemail && !isSelfModifying(packagesModified)) {
+            ContentValues values = new ContentValues();
+            values.put(VoicemailContract.Voicemails.DIRTY, 1);
+            values.put(VoicemailContract.Voicemails.DELETED, 1);
+            count = mDb.update(table, values, whereClause, whereArgs);
+        } else {
+            count = mDb.delete(table, whereClause, whereArgs);
+        }
+
+        if (count > 0 && isVoicemail) {
             notifyVoicemailChange(mBaseUri, packagesModified, Intent.ACTION_PROVIDER_CHANGED);
         }
         if (count > 0 && mIsCallsTable) {
@@ -204,6 +236,11 @@
         return impactedPackages;
     }
 
+    private boolean isSelfModifying(Set<String> packagesModified) {
+        return packagesModified.size() == 1 && getCallingPackages().contains(
+                Iterables.getOnlyElement(packagesModified));
+    }
+
     private void notifyVoicemailChange(Uri notificationUri, Set<String> modifiedPackages,
             String... intentActions) {
         // Notify the observers.
diff --git a/src/com/android/providers/contacts/VoicemailContentTable.java b/src/com/android/providers/contacts/VoicemailContentTable.java
index 16b3df7..9813eea 100644
--- a/src/com/android/providers/contacts/VoicemailContentTable.java
+++ b/src/com/android/providers/contacts/VoicemailContentTable.java
@@ -69,6 +69,8 @@
             .add(Voicemails.SOURCE_PACKAGE)
             .add(Voicemails.HAS_CONTENT)
             .add(Voicemails.MIME_TYPE)
+            .add(Voicemails.DIRTY)
+            .add(Voicemails.DELETED)
             .add(OpenableColumns.DISPLAY_NAME)
             .add(OpenableColumns.SIZE)
             .build();
@@ -99,6 +101,8 @@
                 .add(Voicemails.HAS_CONTENT)
                 .add(Voicemails.MIME_TYPE)
                 .add(Voicemails._DATA)
+                .add(Voicemails.DIRTY)
+                .add(Voicemails.DELETED)
                 .add(OpenableColumns.DISPLAY_NAME, createDisplayName(context))
                 .add(OpenableColumns.SIZE, "NULL")
                 .build();
diff --git a/tests/src/com/android/providers/contacts/BaseVoicemailProviderTest.java b/tests/src/com/android/providers/contacts/BaseVoicemailProviderTest.java
index 547eafa..8e4121d 100644
--- a/tests/src/com/android/providers/contacts/BaseVoicemailProviderTest.java
+++ b/tests/src/com/android/providers/contacts/BaseVoicemailProviderTest.java
@@ -98,6 +98,11 @@
             public File getDir(String name, int mode) {
                 return getTestDirectory();
             }
+
+            @Override
+            public PackageManager getPackageManager() {
+                return new MockPackageManager(mActor.getProviderContext().getPackageName());
+            }
         };
     }
 
@@ -145,6 +150,7 @@
     private interface VvmProviderCalls {
         public void sendOrderedBroadcast(Intent intent, String receiverPermission);
         public File getDir(String name, int mode);
+        public PackageManager getPackageManager();
     }
 
     public static class TestVoicemailProvider extends VoicemailContentProvider {
@@ -171,7 +177,7 @@
                 }
                 @Override
                 public PackageManager getPackageManager() {
-                    return new MockPackageManager("com.test.package1", "com.test.package2");
+                    return mDelegate.getPackageManager();
                 }
             };
         }
diff --git a/tests/src/com/android/providers/contacts/CallLogProviderTest.java b/tests/src/com/android/providers/contacts/CallLogProviderTest.java
index b8233f6..ea436d8 100644
--- a/tests/src/com/android/providers/contacts/CallLogProviderTest.java
+++ b/tests/src/com/android/providers/contacts/CallLogProviderTest.java
@@ -60,7 +60,9 @@
             Voicemails.MIME_TYPE,
             Voicemails.SOURCE_PACKAGE,
             Voicemails.SOURCE_DATA,
-            Voicemails.STATE};
+            Voicemails.STATE,
+            Voicemails.DIRTY,
+            Voicemails.DELETED};
     /** Total number of columns exposed by call_log provider. */
     private static final int NUM_CALLLOG_FIELDS = 25;
 
diff --git a/tests/src/com/android/providers/contacts/VoicemailProviderTest.java b/tests/src/com/android/providers/contacts/VoicemailProviderTest.java
index 4fe1907..1d3ac8a 100644
--- a/tests/src/com/android/providers/contacts/VoicemailProviderTest.java
+++ b/tests/src/com/android/providers/contacts/VoicemailProviderTest.java
@@ -58,7 +58,7 @@
             Calls.COUNTRY_ISO
     };
     /** Total number of columns exposed by voicemail provider. */
-    private static final int NUM_VOICEMAIL_FIELDS = 14;
+    private static final int NUM_VOICEMAIL_FIELDS = 16;
 
     @Override
     protected void setUp() throws Exception {
@@ -120,6 +120,28 @@
         assertStoredValues(uri, values);
     }
 
+    public void testUpdateOwnPackageVoicemail_NotDirty() {
+        final Uri uri = mResolver.insert(voicemailUri(), getTestVoicemailValues());
+        mResolver.update(uri, new ContentValues(), null, null);
+
+        // Updating a package's own voicemail should not make the voicemail dirty.
+        ContentValues values = getTestVoicemailValues();
+        values.put(Voicemails.DIRTY, "0");
+        assertStoredValues(uri, values);
+    }
+
+    public void testUpdateOwnPackageVoicemail_RemovesDirtyStatus() {
+        ContentValues values = getTestVoicemailValues();
+        values.put(Voicemails.DIRTY, "1");
+        final Uri uri = mResolver.insert(voicemailUri(), getTestVoicemailValues());
+
+        mResolver.update(uri, new ContentValues(), null, null);
+        // At this point, the voicemail should be set back to not dirty.
+        ContentValues newValues = getTestVoicemailValues();
+        newValues.put(Voicemails.DIRTY, "0");
+        assertStoredValues(uri, newValues);
+    }
+
     public void testDelete() {
         Uri uri = insertVoicemail();
         int count = mResolver.delete(voicemailUri(), Voicemails._ID + "="
@@ -240,8 +262,10 @@
             }
         });
 
-        // If we have the manage voicemail permission, we should be able to both update and delete
-        // voicemails from all packages
+        // If we have the manage voicemail permission, we should be able to both update voicemails
+        // from all packages. However, when updating or deleting a voicemail from a different
+        // package, the "dirty" flag must be set on updates and "dirty" and "delete" flags must be
+        // set on deletion.
         setUpForNoPermission();
         mActor.addPermissions(WRITE_VOICEMAIL_PERMISSION);
         mResolver.update(anotherVoicemail, getTestVoicemailValues(), null, null);
@@ -254,10 +278,15 @@
 
         mResolver.delete(anotherVoicemail, null, null);
 
-        // Now add the read voicemail permission temporarily to verify that the delete actually
-        // worked
+        // Now add the read voicemail permission temporarily to verify that the delete flag is set.
         mActor.addPermissions(READ_VOICEMAIL_PERMISSION);
-        assertEquals(0, getCount(anotherVoicemail, null, null));
+
+        ContentValues values = getTestVoicemailValues();
+        values.put(Voicemails.DIRTY, "1");
+        values.put(Voicemails.DELETED, "1");
+
+        assertEquals(1, getCount(anotherVoicemail, null, null));
+        assertStoredValues(anotherVoicemail, values);
     }
 
     private Uri withSourcePackageParam(Uri uri) {