Enforce strict grammar when querying the call log

Disallow subqueries and suspicious tokens when querying from the
call log database. Also add a column to the projection so that the token
detector doesn't throw a false positive.

Bug: 143230980
Test: atest android.provider.cts.contacts.CallLogProviderTest
Change-Id: I54d2d399f449ea47aa8fb4197c718f9038da25bf
(cherry picked from commit 1e54cb39bb76f5cc12526abca99b1af017d7d3d2)
Merged-In: I54d2d399f449ea47aa8fb4197c718f9038da25bf
diff --git a/src/com/android/providers/contacts/CallLogProvider.java b/src/com/android/providers/contacts/CallLogProvider.java
index ec1d40e..ebe111f 100644
--- a/src/com/android/providers/contacts/CallLogProvider.java
+++ b/src/com/android/providers/contacts/CallLogProvider.java
@@ -138,6 +138,7 @@
         sCallsProjectionMap.put(Calls.FEATURES, Calls.FEATURES);
         sCallsProjectionMap.put(Calls.PHONE_ACCOUNT_COMPONENT_NAME, Calls.PHONE_ACCOUNT_COMPONENT_NAME);
         sCallsProjectionMap.put(Calls.PHONE_ACCOUNT_ID, Calls.PHONE_ACCOUNT_ID);
+        sCallsProjectionMap.put(Calls.PHONE_ACCOUNT_HIDDEN, Calls.PHONE_ACCOUNT_HIDDEN);
         sCallsProjectionMap.put(Calls.PHONE_ACCOUNT_ADDRESS, Calls.PHONE_ACCOUNT_ADDRESS);
         sCallsProjectionMap.put(Calls.NEW, Calls.NEW);
         sCallsProjectionMap.put(Calls.VOICEMAIL_URI, Calls.VOICEMAIL_URI);
@@ -324,6 +325,13 @@
         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);
+        }
 
         final SelectionBuilder selectionBuilder = new SelectionBuilder(selection);
         checkVoicemailPermissionAndAddRestriction(uri, selectionBuilder, true /*isQuery*/);
@@ -529,6 +537,18 @@
         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);
+        }
+
         final SQLiteDatabase db = mDbHelper.getWritableDatabase();
         final int matchedUriId = sURIMatcher.match(uri);
         switch (matchedUriId) {
@@ -543,8 +563,7 @@
                 throw new UnsupportedOperationException("Cannot update URL: " + uri);
         }
 
-        return createDatabaseModifier(db).update(uri, Tables.CALLS, values, selectionBuilder.build(),
-                selectionArgs);
+        return qb.update(db, values, selectionBuilder.build(), selectionArgs);
     }
 
     private int deleteInternal(Uri uri, String selection, String[] selectionArgs) {
@@ -559,14 +578,25 @@
         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);
+        }
+
         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 createDatabaseModifier(db).delete(Tables.CALLS,
-                        selectionBuilder.build(), selectionArgs);
+                return qb.delete(db, selectionBuilder.build(), selectionArgs);
             default:
                 throw new UnsupportedOperationException("Cannot delete that URL: " + uri);
         }
diff --git a/tests/src/com/android/providers/contacts/CallLogProviderTest.java b/tests/src/com/android/providers/contacts/CallLogProviderTest.java
index 9efdfaa..9baf1e4 100644
--- a/tests/src/com/android/providers/contacts/CallLogProviderTest.java
+++ b/tests/src/com/android/providers/contacts/CallLogProviderTest.java
@@ -18,6 +18,7 @@
 
 import android.telecom.CallerInfo;
 import com.android.providers.contacts.testutil.CommonDatabaseUtils;
+import com.android.providers.contacts.util.ContactsPermissions;
 
 import android.content.ComponentName;
 import android.content.ContentProvider;
@@ -59,7 +60,7 @@
             Voicemails.DIRTY,
             Voicemails.DELETED};
     /** Total number of columns exposed by call_log provider. */
-    private static final int NUM_CALLLOG_FIELDS = 34;
+    private static final int NUM_CALLLOG_FIELDS = 35;
 
     private static final int MIN_MATCH = 7;
 
@@ -194,9 +195,12 @@
         PhoneAccountHandle subscription = new PhoneAccountHandle(
                 sComponentName, "sub0");
 
+        // Allow self-calls in order to add the call
+        ContactsPermissions.ALLOW_SELF_CALL = true;
         Uri uri = Calls.addCall(ci, getMockContext(), "1-800-263-7643",
                 Calls.PRESENTATION_ALLOWED, Calls.OUTGOING_TYPE, 0, subscription, 2000,
                 40, null);
+        ContactsPermissions.ALLOW_SELF_CALL = false;
         assertNotNull(uri);
         assertEquals("0@" + CallLog.AUTHORITY, uri.getAuthority());