Merge cherrypicks of [16010620, 16011373, 16011377, 16011394, 16011395, 16011396, 16011398, 16011400, 16011402, 16012407, 16010623, 16011574, 16011575, 16010923, 16011434, 16011691, 16011382, 16011383, 16011303, 16011385, 16011579] into security-aosp-rvc-release

Change-Id: I6af30ee1cbc4b9aab69d491c6169829c4f61f68a
diff --git a/src/com/android/providers/contacts/CallLogProvider.java b/src/com/android/providers/contacts/CallLogProvider.java
index ebe111f..3ee4ae9 100644
--- a/src/com/android/providers/contacts/CallLogProvider.java
+++ b/src/com/android/providers/contacts/CallLogProvider.java
@@ -121,7 +121,7 @@
         sURIMatcher.addURI(CallLog.SHADOW_AUTHORITY, "calls", CALLS);
     }
 
-    private static final ArrayMap<String, String> sCallsProjectionMap;
+    public static final ArrayMap<String, String> sCallsProjectionMap;
     static {
 
         // Calls projection map
@@ -536,19 +536,8 @@
 
         SelectionBuilder selectionBuilder = new SelectionBuilder(selection);
         checkVoicemailPermissionAndAddRestriction(uri, selectionBuilder, false /*isQuery*/);
-
-        final SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
-        qb.setTables(Tables.CALLS);
-        qb.setProjectionMap(sCallsProjectionMap);
-        qb.setStrict(true);
-        // If the caller doesn't have READ_VOICEMAIL, make sure they can't
-        // do any SQL shenanigans to get access to the voicemails. If the caller does have the
-        // READ_VOICEMAIL permission, then they have sufficient permissions to access any data in
-        // the database, so the strict check is unnecessary.
-        if (!mVoicemailPermissions.callerHasReadAccess(getCallingPackage())) {
-            qb.setStrictGrammar(true);
-        }
-
+        boolean hasReadVoicemailPermission = mVoicemailPermissions.callerHasReadAccess(
+                getCallingPackage());
         final SQLiteDatabase db = mDbHelper.getWritableDatabase();
         final int matchedUriId = sURIMatcher.match(uri);
         switch (matchedUriId) {
@@ -563,7 +552,8 @@
                 throw new UnsupportedOperationException("Cannot update URL: " + uri);
         }
 
-        return qb.update(db, values, selectionBuilder.build(), selectionArgs);
+        return createDatabaseModifier(db, hasReadVoicemailPermission).update(uri, Tables.CALLS,
+                values, selectionBuilder.build(), selectionArgs);
     }
 
     private int deleteInternal(Uri uri, String selection, String[] selectionArgs) {
@@ -578,25 +568,14 @@
         SelectionBuilder selectionBuilder = new SelectionBuilder(selection);
         checkVoicemailPermissionAndAddRestriction(uri, selectionBuilder, false /*isQuery*/);
 
-        final SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
-        qb.setTables(Tables.CALLS);
-        qb.setProjectionMap(sCallsProjectionMap);
-        qb.setStrict(true);
-        // If the caller doesn't have READ_VOICEMAIL, make sure they can't
-        // do any SQL shenanigans to get access to the voicemails. If the caller does have the
-        // READ_VOICEMAIL permission, then they have sufficient permissions to access any data in
-        // the database, so the strict check is unnecessary.
-        if (!mVoicemailPermissions.callerHasReadAccess(getCallingPackage())) {
-            qb.setStrictGrammar(true);
-        }
-
+        boolean hasReadVoicemailPermission =
+                mVoicemailPermissions.callerHasReadAccess(getCallingPackage());
         final SQLiteDatabase db = mDbHelper.getWritableDatabase();
         final int matchedUriId = sURIMatcher.match(uri);
         switch (matchedUriId) {
             case CALLS:
-                // TODO: Special case - We may want to forward the delete request on user 0 to the
-                // shadow provider too.
-                return qb.delete(db, selectionBuilder.build(), selectionArgs);
+                return createDatabaseModifier(db, hasReadVoicemailPermission).delete(Tables.CALLS,
+                    selectionBuilder.build(), selectionArgs);
             default:
                 throw new UnsupportedOperationException("Cannot delete that URL: " + uri);
         }
@@ -610,8 +589,9 @@
      * Returns a {@link DatabaseModifier} that takes care of sending necessary notifications
      * after the operation is performed.
      */
-    private DatabaseModifier createDatabaseModifier(SQLiteDatabase db) {
-        return new DbModifierWithNotification(Tables.CALLS, db, getContext());
+    private DatabaseModifier createDatabaseModifier(SQLiteDatabase db, boolean hasReadVoicemail) {
+        return new DbModifierWithNotification(Tables.CALLS, db, null, hasReadVoicemail,
+                getContext());
     }
 
     /**
diff --git a/src/com/android/providers/contacts/DbModifierWithNotification.java b/src/com/android/providers/contacts/DbModifierWithNotification.java
index 852301d..dc74c0a 100644
--- a/src/com/android/providers/contacts/DbModifierWithNotification.java
+++ b/src/com/android/providers/contacts/DbModifierWithNotification.java
@@ -27,6 +27,7 @@
 import android.database.Cursor;
 import android.database.DatabaseUtils.InsertHelper;
 import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteQueryBuilder;
 import android.net.Uri;
 import android.os.Binder;
 import android.provider.CallLog.Calls;
@@ -65,6 +66,7 @@
             Voicemails.DELETED + " == 0";
     private final String mTableName;
     private final SQLiteDatabase mDb;
+    private final boolean mHasReadVoicemailPermission;
     private final InsertHelper mInsertHelper;
     private final Context mContext;
     private final Uri mBaseUri;
@@ -86,8 +88,14 @@
 
     private DbModifierWithNotification(String tableName, SQLiteDatabase db,
             InsertHelper insertHelper, Context context) {
+        this(tableName, db, insertHelper, true /* hasReadVoicemail */, context);
+    }
+
+    public DbModifierWithNotification(String tableName, SQLiteDatabase db,
+            InsertHelper insertHelper, boolean hasReadVoicemailPermission, Context context) {
         mTableName = tableName;
         mDb = db;
+        mHasReadVoicemailPermission = hasReadVoicemailPermission;
         mInsertHelper = insertHelper;
         mContext = context;
         mBaseUri = mTableName.equals(Tables.VOICEMAIL_STATUS) ?
@@ -109,7 +117,7 @@
                     packagesModified);
         }
         if (rowId > 0 && mIsCallsTable) {
-            notifyCallLogChange();
+            notifyCallLogChange(mContext);
         }
         return rowId;
     }
@@ -126,20 +134,20 @@
                     ContentUris.withAppendedId(mBaseUri, rowId), packagesModified);
         }
         if (rowId > 0 && mIsCallsTable) {
-            notifyCallLogChange();
+            notifyCallLogChange(mContext);
         }
         return rowId;
     }
 
-    private void notifyCallLogChange() {
-        mContext.getContentResolver().notifyChange(Calls.CONTENT_URI, null, false);
+    public static void notifyCallLogChange(Context context) {
+        context.getContentResolver().notifyChange(Calls.CONTENT_URI, null, false);
 
         Intent intent = new Intent("com.android.internal.action.CALL_LOG_CHANGE");
         intent.setComponent(new ComponentName("com.android.calllogbackup",
                 "com.android.calllogbackup.CallLogChangeReceiver"));
 
-        if (!mContext.getPackageManager().queryBroadcastReceivers(intent, 0).isEmpty()) {
-            mContext.sendBroadcast(intent);
+        if (!context.getPackageManager().queryBroadcastReceivers(intent, 0).isEmpty()) {
+            context.sendBroadcast(intent);
         }
     }
 
@@ -196,12 +204,21 @@
         if (values.isEmpty()) {
             return 0;
         }
-        int count = mDb.update(table, values, whereClause, whereArgs);
+
+        final SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
+        qb.setTables(mTableName);
+        qb.setProjectionMap(CallLogProvider.sCallsProjectionMap);
+        qb.setStrict(true);
+        if (!mHasReadVoicemailPermission) {
+            qb.setStrictGrammar(true);
+        }
+        int count = qb.update(mDb, values, whereClause, whereArgs);
+
         if (count > 0 && isVoicemailContent || Tables.VOICEMAIL_STATUS.equals(table)) {
             notifyVoicemailChange(mBaseUri, packagesModified);
         }
         if (count > 0 && mIsCallsTable) {
-            notifyCallLogChange();
+            notifyCallLogChange(mContext);
         }
         if (hasMarkedRead) {
             // A "New" voicemail has been marked as read by the server. This voicemail is no longer
@@ -269,21 +286,30 @@
         // If the deletion is being made by the package that inserted the voicemail or by
         // CP2 (cleanup after uninstall), then we don't need to wait for sync, so just delete it.
         final int count;
+
+        final SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
+        qb.setTables(mTableName);
+        qb.setProjectionMap(CallLogProvider.sCallsProjectionMap);
+        qb.setStrict(true);
+        if (!mHasReadVoicemailPermission) {
+            qb.setStrictGrammar(true);
+        }
+
         if (mIsCallsTable && isVoicemail && !isSelfModifyingOrInternal(packagesModified)) {
             ContentValues values = new ContentValues();
             values.put(VoicemailContract.Voicemails.DIRTY, 1);
             values.put(VoicemailContract.Voicemails.DELETED, 1);
             values.put(VoicemailContract.Voicemails.LAST_MODIFIED, getTimeMillis());
-            count = mDb.update(table, values, whereClause, whereArgs);
+            count = qb.update(mDb, values, whereClause, whereArgs);
         } else {
-            count = mDb.delete(table, whereClause, whereArgs);
+            count = qb.delete(mDb, whereClause, whereArgs);
         }
 
         if (count > 0 && isVoicemail) {
             notifyVoicemailChange(mBaseUri, packagesModified);
         }
         if (count > 0 && mIsCallsTable) {
-            notifyCallLogChange();
+            notifyCallLogChange(mContext);
         }
         return count;
     }