1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.provider.cts.contacts; 18 19 import android.content.ContentProviderClient; 20 import android.content.ContentResolver; 21 import android.content.ContentUris; 22 import android.content.ContentValues; 23 import android.database.ContentObserver; 24 import android.database.Cursor; 25 import android.net.Uri; 26 import android.platform.test.annotations.AsbSecurityTest; 27 import android.provider.CallLog; 28 import android.provider.CallLog.Calls; 29 import android.test.InstrumentationTestCase; 30 31 import java.util.concurrent.CountDownLatch; 32 import java.util.concurrent.TimeUnit; 33 34 public class CallLogProviderTest extends InstrumentationTestCase { 35 private ContentResolver mContentResolver; 36 private ContentProviderClient mProvider; 37 38 private static final String TEST_NUMBER = "5551234"; 39 private static final int TIME_OUT_MILLIS = 5000; 40 41 @Override setUp()42 protected void setUp() throws Exception { 43 super.setUp(); 44 mContentResolver = getInstrumentation().getTargetContext().getContentResolver(); 45 mProvider = mContentResolver.acquireContentProviderClient(CallLog.AUTHORITY); 46 } 47 48 @AsbSecurityTest(cveBugId = 143230980) testNoSubqueries()49 public void testNoSubqueries() throws Exception { 50 // Add a single call just to make sure the call log has something inside 51 ContentValues values = new ContentValues(); 52 values.put(CallLog.Calls.NUMBER, TEST_NUMBER); 53 values.put(CallLog.Calls.TYPE, Calls.OUTGOING_TYPE); 54 values.put(CallLog.Calls.DATE, Long.valueOf(0 /*start time*/)); 55 values.put(CallLog.Calls.DURATION, Long.valueOf(5 /*call duration*/)); 56 57 mContentResolver.insert(CallLog.Calls.CONTENT_URI, values); 58 59 // Attempt to do a query that contains a subquery -- this should fail since this test does 60 // not have READ_VOICEMAIL. 61 try { 62 Cursor c = mProvider.query(Calls.CONTENT_URI, null, CallLog.Calls.NUMBER + " = ?", 63 new String[]{TEST_NUMBER}, 64 "date DESC LIMIT (SELECT count(*) + 1 FROM calls WHERE type = 4"); 65 assertEquals(0, c.getCount()); 66 } catch (IllegalArgumentException e) { 67 // expected/tolerated 68 } 69 } 70 testUpdate()71 public void testUpdate() throws Exception { 72 // Add a single call just to make sure the call log has something inside 73 ContentValues values = new ContentValues(); 74 values.put(CallLog.Calls.NUMBER, TEST_NUMBER); 75 values.put(CallLog.Calls.TYPE, Calls.OUTGOING_TYPE); 76 values.put(CallLog.Calls.DATE, Long.valueOf(0 /*start time*/)); 77 values.put(CallLog.Calls.DURATION, Long.valueOf(5 /*call duration*/)); 78 Uri uri = mContentResolver.insert(CallLog.Calls.CONTENT_URI, values); 79 // Get last modified prior to modification. 80 long lastModified = getLastModified(uri); 81 82 CountDownLatch changeLatch = new CountDownLatch(1); 83 mContentResolver.registerContentObserver( 84 CallLog.Calls.CONTENT_URI, true, 85 new ContentObserver(null /* handler */) { 86 @Override 87 public void onChange(boolean selfChange, Uri uri) { 88 mContentResolver.unregisterContentObserver(this); 89 changeLatch.countDown(); 90 super.onChange(selfChange); 91 } 92 }); 93 94 // Update it! 95 values.put(CallLog.Calls.DURATION, Long.valueOf(6 /*call duration*/)); 96 int numUpdated = mContentResolver.update(uri, values, null, null); 97 assertEquals(1, numUpdated); 98 try { 99 assertTrue(changeLatch.await(TIME_OUT_MILLIS, TimeUnit.MILLISECONDS)); 100 } catch (InterruptedException e) { 101 e.printStackTrace(); 102 fail("Expected update notification."); 103 } 104 105 // Verify last modified is > than the original value. 106 long newLastModified = getLastModified(uri); 107 assertTrue(newLastModified > lastModified); 108 } 109 getLastModified(Uri uri)110 public long getLastModified(Uri uri) { 111 // Query the data base to get the last updated. 112 String[] projection = new String[] { 113 Calls.LAST_MODIFIED 114 }; 115 Cursor results = mContentResolver.query(uri, projection, null, null, null, null); 116 results.moveToFirst(); 117 assertEquals(1, results.getCount()); 118 return results.getLong(results.getColumnIndex(Calls.LAST_MODIFIED)); 119 } 120 testDelete()121 public void testDelete() throws Exception { 122 // Add a single call just to make sure the call log has something inside 123 ContentValues values = new ContentValues(); 124 values.put(CallLog.Calls.NUMBER, TEST_NUMBER); 125 values.put(CallLog.Calls.TYPE, Calls.OUTGOING_TYPE); 126 values.put(CallLog.Calls.DATE, Long.valueOf(0 /*start time*/)); 127 values.put(CallLog.Calls.DURATION, Long.valueOf(5 /*call duration*/)); 128 Uri uri = mContentResolver.insert(CallLog.Calls.CONTENT_URI, values); 129 130 CountDownLatch changeLatch = new CountDownLatch(1); 131 mContentResolver.registerContentObserver( 132 CallLog.Calls.CONTENT_URI, true, 133 new ContentObserver(null /* handler */) { 134 @Override 135 public void onChange(boolean selfChange, Uri uri) { 136 mContentResolver.unregisterContentObserver(this); 137 changeLatch.countDown(); 138 super.onChange(selfChange); 139 } 140 }); 141 142 // Delete it. 143 // Yuck, you can't just delete using the uri passed in; you need to build a where clause. 144 int count = mContentResolver.delete(Calls.CONTENT_URI, Calls._ID + "=" 145 + ContentUris.parseId(uri), null); 146 assertEquals(1, count); 147 try { 148 assertTrue(changeLatch.await(TIME_OUT_MILLIS, TimeUnit.MILLISECONDS)); 149 } catch (InterruptedException e) { 150 e.printStackTrace(); 151 fail("Expected update notification."); 152 } 153 } 154 } 155