Merge tag 'android-security-11.0.0_r50' into int/11/fp3

Android security 11.0.0 release 50

* tag 'android-security-11.0.0_r50':
  Fix issue with call log last modified not being updated.
  Ensure update/deletes in the call log notify content observers.

Change-Id: I11b26cbeff41127ee0f8fd3aa04ca625a64af48c
diff --git a/src/com/android/providers/contacts/CallLogProvider.java b/src/com/android/providers/contacts/CallLogProvider.java
index 7a61015..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,11 +552,8 @@
                 throw new UnsupportedOperationException("Cannot update URL: " + uri);
         }
 
-        int rowsUpdated = qb.update(db, values, selectionBuilder.build(), selectionArgs);
-        if (rowsUpdated > 0) {
-            DbModifierWithNotification.notifyCallLogChange(getContext());
-        }
-        return rowsUpdated;
+        return createDatabaseModifier(db, hasReadVoicemailPermission).update(uri, Tables.CALLS,
+                values, selectionBuilder.build(), selectionArgs);
     }
 
     private int deleteInternal(Uri uri, String selection, String[] selectionArgs) {
@@ -582,29 +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.
-                int deletedCount = qb.delete(db, selectionBuilder.build(), selectionArgs);
-                if (deletedCount > 0) {
-                    DbModifierWithNotification.notifyCallLogChange(getContext());
-                }
-                return deletedCount;
+                return createDatabaseModifier(db, hasReadVoicemailPermission).delete(Tables.CALLS,
+                    selectionBuilder.build(), selectionArgs);
             default:
                 throw new UnsupportedOperationException("Cannot delete that URL: " + uri);
         }
@@ -618,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 03ebd1f..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) ?
@@ -196,7 +204,16 @@
         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);
         }
@@ -269,14 +286,23 @@
         // 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) {